diff --git a/docs/compiled/artplayer-plugin-asr.js b/docs/compiled/artplayer-plugin-asr.js index f218500a2..1aa5c71ce 100644 --- a/docs/compiled/artplayer-plugin-asr.js +++ b/docs/compiled/artplayer-plugin-asr.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=1);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),i.connect(n),n.connect(e.destination)}volume(t){this.gain&&(this.gain.gain.value=t)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,n=Math.floor(e*s/1e3);if(!Number.isSafeInteger(n)||n<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const r=this.graph??(this.graph=new t(this.video,e));await r.prepare(),i()?(r.connect(t=>{if(this.running&&this.graph===r)return this.queue.length+t.length>Math.max(60*e,2*n)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(n),s)):this.running||this.graph!==r||r.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),c=new i(t.video,{interval:s,sampleRate:n,onAudioChunk:o},h.append),l=()=>c.volume(t.volume),d=()=>c.start(),u=()=>c.pause(),p=()=>c.restart(),g=()=>(h.destroy(),t.off("video:volumechange",l),t.off("play",d),t.off("pause",u),t.off("restart",p),t.off("destroy",g),c.stop(!0));return t.on("video:volumechange",l),t.on("play",d),t.on("pause",u),t.on("restart",p),t.on("destroy",g),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const r=this.gain=s||e.createGain();s||(r.gain.value=1);const n=this.recorder=new AudioWorkletNode(e,"recorder-processor");n.port.onmessage=e=>{this.closed||this.recorder!==n||t(e.data)},i.connect(n),i.connect(r),r.connect(e.destination)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,r=Math.floor(e*s/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const n=this.graph??(this.graph=new t(this.video,e));await n.prepare(),i()?(n.connect(t=>{if(this.running&&this.graph===n)return this.queue.length+t.length>Math.max(60*e,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),s)):this.running||this.graph!==n||n.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:r=16e3,autoHideTimeout:n=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,r=!1;function n(){clearTimeout(s),s=void 0}function o(){r||(t.style.display="none")}return{append:function(a){r||"string"!==typeof a||(n(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){r=!0,n()}}}(a,e,n),c=new i(t.video,{interval:s,sampleRate:r,onAudioChunk:o},h.append),l=()=>c.start(),d=()=>c.pause(),p=()=>c.restart(),u=()=>(h.destroy(),t.off("play",l),t.off("pause",d),t.off("restart",p),t.off("destroy",u),c.stop(!0));return t.on("play",l),t.on("pause",d),t.on("restart",p),t.on("destroy",u),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); diff --git a/docs/compiled/artplayer-plugin-asr.legacy.js b/docs/compiled/artplayer-plugin-asr.legacy.js index 3a00665c7..b44f4b3f5 100644 --- a/docs/compiled/artplayer-plugin-asr.legacy.js +++ b/docs/compiled/artplayer-plugin-asr.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=1);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),i.connect(n),n.connect(e.destination)}volume(t){this.gain&&(this.gain.gain.value=t)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),l=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o},h.append),c=()=>l.volume(t.volume),d=()=>l.start(),u=()=>l.pause(),p=()=>l.restart(),f=()=>(h.destroy(),t.off("video:volumechange",c),t.off("play",d),t.off("pause",u),t.off("restart",p),t.off("destroy",f),l.stop(!0));return t.on("video:volumechange",c),t.on("play",d),t.on("pause",u),t.on("restart",p),t.on("destroy",f),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=1);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),i.connect(n),n.connect(e.destination)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),l=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o},h.append),c=()=>l.start(),d=()=>l.pause(),u=()=>l.restart(),p=()=>(h.destroy(),t.off("play",c),t.off("pause",d),t.off("restart",u),t.off("destroy",p),l.stop(!0));return t.on("play",c),t.on("pause",d),t.on("restart",u),t.on("destroy",p),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); diff --git a/docs/compiled/artplayer-plugin-asr.mjs b/docs/compiled/artplayer-plugin-asr.mjs index 3da312943..fea4c7923 100644 --- a/docs/compiled/artplayer-plugin-asr.mjs +++ b/docs/compiled/artplayer-plugin-asr.mjs @@ -109,10 +109,6 @@ class AudioGraph { source.connect(gain); gain.connect(context.destination); } - volume(value) { - if (this.gain) - this.gain.gain.value = value; - } disconnect() { if (this.recorder) { this.recorder.port.onmessage = null; @@ -313,9 +309,6 @@ class Capture { this.pending = void 0; } } - volume(value) { - this.graph?.volume(value); - } stop(terminal = false) { this.terminal || (this.terminal = terminal); this.pause(); @@ -364,21 +357,18 @@ function artplayerPluginAsr(option = {}) { throw new Error("Could not create ASR subtitle layer"); const subtitles = createSubtitles(layer, length, autoHideTimeout); const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk }, subtitles.append); - const volume = () => capture.volume(art.volume); const play = () => capture.start(); const pause = () => capture.pause(); const restart = () => capture.restart(); const stop = () => capture.stop(); const destroy = () => { subtitles.destroy(); - art.off("video:volumechange", volume); art.off("play", play); art.off("pause", pause); art.off("restart", restart); art.off("destroy", destroy); return capture.stop(true); }; - art.on("video:volumechange", volume); art.on("play", play); art.on("pause", pause); art.on("restart", restart); diff --git a/packages/artplayer-plugin-asr/ARCHITECTURE.md b/packages/artplayer-plugin-asr/ARCHITECTURE.md index 35753e613..410104869 100644 --- a/packages/artplayer-plugin-asr/ARCHITECTURE.md +++ b/packages/artplayer-plugin-asr/ARCHITECTURE.md @@ -34,6 +34,13 @@ continues through its playback gain to the destination. A source switch reuses t direct source and replaces the recorder; a captured-stream fallback is closed and reacquired because captured tracks can end on source changes. +The playback gain stays at unity. Native media-element source output already +reflects the video's volume and mute; applying the same volume again would turn +50% volume into 25% output amplitude. PCM also follows the main video's volume +and becomes silent when that element is muted. Native analyser tests cover the +linear output ratio, mute, stop and resume on Chromium/Firefox; they do not measure +physical speakers or establish Safari/device support. + `stop()` returns `Promise` and releases ASR work. An already displayed subtitle retains its original auto-hide deadline; obsolete callbacks cannot add timers. It retains playback subscriptions and allows explicit append or a later play. @@ -85,6 +92,9 @@ From the repository root: - `yarn test:browser test/browser/asr-audio.spec.js`: local AAC, actual Worklet, published/current core, pause/resume, source switch, stop/restart and destroy. Set `ARTPLAYER_ASR_ARTIFACT` to an actual main/legacy file to test that artifact. +- `yarn test:browser test/browser/asr-playback.spec.js`: frozen 2.1 output + attenuation versus candidate linear volume, mute and stop/resume. A native + unity analyser measures the gain-to-destination route with local audio. - `yarn typecheck`: includes the package strict/noUncheckedIndexedAccess project. - `yarn test:asr-types-package`: packs and installs core/ASR outside the workspace, checks frozen 2.0/2.1 consumers and candidate declarations in strict compiler diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js index f218500a2..1aa5c71ce 100644 --- a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=1);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),i.connect(n),n.connect(e.destination)}volume(t){this.gain&&(this.gain.gain.value=t)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,n=Math.floor(e*s/1e3);if(!Number.isSafeInteger(n)||n<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const r=this.graph??(this.graph=new t(this.video,e));await r.prepare(),i()?(r.connect(t=>{if(this.running&&this.graph===r)return this.queue.length+t.length>Math.max(60*e,2*n)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(n),s)):this.running||this.graph!==r||r.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),c=new i(t.video,{interval:s,sampleRate:n,onAudioChunk:o},h.append),l=()=>c.volume(t.volume),d=()=>c.start(),u=()=>c.pause(),p=()=>c.restart(),g=()=>(h.destroy(),t.off("video:volumechange",l),t.off("play",d),t.off("pause",u),t.off("restart",p),t.off("destroy",g),c.stop(!0));return t.on("video:volumechange",l),t.on("play",d),t.on("pause",u),t.on("restart",p),t.on("destroy",g),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";class t{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}async initialize(){this.assertOpen();const t=window,e=window.AudioContext||t.webkitAudioContext;if(!e)throw new Error("AudioContext is not supported");const i=this.context??(this.context=new e({sampleRate:this.sampleRate}));if("suspended"===i.state&&await i.resume(),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{await i.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=i.createMediaElementSource(this.video),this.direct=!0}catch(s){console.warn("[artplayerPluginAsr] Direct connection failed:",s);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=i.createMediaStreamSource(this.stream)}}prepare(){return this.preparation??(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const r=this.gain=s||e.createGain();s||(r.gain.value=1);const n=this.recorder=new AudioWorkletNode(e,"recorder-processor");n.port.onmessage=e=>{this.closed||this.recorder!==n||t(e.data)},i.connect(n),i.connect(r),r.connect(e.destination)}disconnect(){this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),this.gain?.disconnect(),this.gain=void 0,this.source?.disconnect()}async close(){if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),this.stream?.getTracks().forEach(t=>t.stop()),this.stream=void 0;const t=this.context;this.context=void 0,this.source=void 0,t&&"closed"!==t.state&&await t.close()}}class e{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const e=this.epoch,i=()=>!this.terminal&&this.epoch===e,s=(async()=>{if(await this.closing,!i())return;const{sampleRate:e,interval:s}=this.options,r=Math.floor(e*s/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(s)||s<=0)throw new Error("Audio chunk length must be positive and finite");const n=this.graph??(this.graph=new t(this.video,e));await n.prepare(),i()?(n.connect(t=>{if(this.running&&this.graph===n)return this.queue.length+t.length>Math.max(60*e,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),s)):this.running||this.graph!==n||n.idle()})().catch(async t=>{i()&&(console.error("[artplayerPluginAsr] Initialization failed:",t),await this.stop())}).finally(()=>{this.starting===s&&(this.starting=void 0)});return this.starting=s,s}async tick(t){if(!this.running||this.pending)return;const e=this.queue.take(t);if(!e)return;const i=this.epoch,s=this.pending={};try{const t=await this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function s(t={}){const{length:e=3,interval:s=100,sampleRate:r=16e3,autoHideTimeout:n=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,r=!1;function n(){clearTimeout(s),s=void 0}function o(){r||(t.style.display="none")}return{append:function(a){r||"string"!==typeof a||(n(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(/(?<=[\u3001\u3002\uff01\uff1f!?.])\s*/u).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){r=!0,n()}}}(a,e,n),c=new i(t.video,{interval:s,sampleRate:r,onAudioChunk:o},h.append),l=()=>c.start(),d=()=>c.pause(),p=()=>c.restart(),u=()=>(h.destroy(),t.off("play",l),t.off("pause",d),t.off("restart",p),t.off("destroy",u),c.stop(!0));return t.on("play",l),t.on("pause",d),t.on("restart",p),t.on("destroy",u),{name:"artplayerPluginAsr",stop:()=>c.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(s,"default",{value:s}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return s}); diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js index 3a00665c7..b44f4b3f5 100644 --- a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=1);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),i.connect(n),n.connect(e.destination)}volume(t){this.gain&&(this.gain.gain.value=t)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),l=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o},h.append),c=()=>l.volume(t.volume),d=()=>l.start(),u=()=>l.pause(),p=()=>l.restart(),f=()=>(h.destroy(),t.off("video:volumechange",c),t.off("play",d),t.off("pause",u),t.off("restart",p),t.off("destroy",f),l.stop(!0));return t.on("video:volumechange",c),t.on("play",d),t.on("pause",u),t.on("restart",p),t.on("destroy",f),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginAsr=e(),define(function(){return t.artplayerPluginAsr})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAsr=e()}(this,function(){"use strict";var t=(t,e,i)=>new Promise((s,n)=>{var r=t=>{try{a(i.next(t))}catch(e){n(e)}},o=t=>{try{a(i.throw(t))}catch(e){n(e)}},a=t=>t.done?s(t.value):Promise.resolve(t.value).then(r,o);a((i=i.apply(t,e)).next())});class e{constructor(t,e){this.video=t,this.sampleRate=e,this.closed=!1,this.direct=!1,this.workletLoaded=!1}assertOpen(){if(this.closed)throw new Error("Audio capture was closed")}revokeWorklet(){void 0!==this.blobUrl&&(URL.revokeObjectURL(this.blobUrl),this.blobUrl=void 0)}initialize(){return t(this,null,function*(){var t;this.assertOpen();const e=window,i=window.AudioContext||e.webkitAudioContext;if(!i)throw new Error("AudioContext is not supported");const s=null!=(t=this.context)?t:this.context=new i({sampleRate:this.sampleRate});if("suspended"===s.state&&(yield s.resume()),this.assertOpen(),!this.workletLoaded){this.blobUrl=URL.createObjectURL(new Blob(["\nclass RecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n const input = inputs[0];\n if (input && input[0]) {\n this.port.postMessage(input[0]);\n }\n return true;\n }\n}\nregisterProcessor('recorder-processor', RecorderProcessor);\n"],{type:"application/javascript"}));try{yield s.audioWorklet.addModule(this.blobUrl),this.workletLoaded=!0}finally{this.revokeWorklet()}}if(this.assertOpen(),!this.source)try{this.source=s.createMediaElementSource(this.video),this.direct=!0}catch(n){console.warn("[artplayerPluginAsr] Direct connection failed:",n);const t=this.video.captureStream||this.video.mozCaptureStream;if(!t)throw new Error("Could not establish audio source");this.stream=t.call(this.video),this.source=s.createMediaStreamSource(this.stream)}})}prepare(){return null!=this.preparation||(this.preparation=this.initialize().catch(t=>{throw this.preparation=void 0,t})),this.preparation}get ownsMediaConnection(){return this.direct}idle(){const t=this.gain;this.disconnect(),this.direct&&this.context&&this.source&&(this.gain=t||this.context.createGain(),t||(this.gain.gain.value=1),this.source.connect(this.gain),this.gain.connect(this.context.destination))}connect(t){this.assertOpen();const e=this.context,i=this.source;if(!e||!i)throw new Error("Audio capture is not prepared");const s=this.gain;this.disconnect();const n=this.gain=s||e.createGain();s||(n.gain.value=1);const r=this.recorder=new AudioWorkletNode(e,"recorder-processor");r.port.onmessage=e=>{this.closed||this.recorder!==r||t(e.data)},i.connect(r),i.connect(n),n.connect(e.destination)}disconnect(){var t,e;this.recorder&&(this.recorder.port.onmessage=null,this.recorder.disconnect(),this.recorder=void 0),null==(t=this.gain)||t.disconnect(),this.gain=void 0,null==(e=this.source)||e.disconnect()}close(){return t(this,null,function*(){var t;if(this.closed)return;this.closed=!0,this.disconnect(),this.revokeWorklet(),null==(t=this.stream)||t.getTracks().forEach(t=>t.stop()),this.stream=void 0;const e=this.context;this.context=void 0,this.source=void 0,e&&"closed"!==e.state&&(yield e.close())})}}class i{constructor(){this.offset=0,this.size=0}get length(){return this.size}push(t){if(0===t.length)return;const e={samples:new Float32Array(t)};this.tail?this.tail.next=e:this.head=e,this.tail=e,this.size+=t.length}take(t){if(!Number.isSafeInteger(t)||t<=0)throw new RangeError("Sample count must be a positive safe integer");if(t>this.size)return null;const e=new Float32Array(t);let i=0;for(;this.head&&it?this.start():void 0):t?this.start():Promise.resolve()}start(){if(this.terminal||this.running)return Promise.resolve();if(this.starting)return this.starting;const i=this.epoch,s=()=>!this.terminal&&this.epoch===i,n=(()=>t(this,null,function*(){var t;if(yield this.closing,!s())return;const{sampleRate:i,interval:n}=this.options,r=Math.floor(i*n/1e3);if(!Number.isSafeInteger(r)||r<1||!Number.isFinite(n)||n<=0)throw new Error("Audio chunk length must be positive and finite");const o=null!=(t=this.graph)?t:this.graph=new e(this.video,i);yield o.prepare(),s()?(o.connect(t=>{if(this.running&&this.graph===o)return this.queue.length+t.length>Math.max(60*i,2*r)?(console.error("[artplayerPluginAsr] Audio callback backlog exceeded its capture limit"),void this.pause()):void this.queue.push(t)}),this.running=!0,this.timer=setInterval(()=>this.tick(r),n)):this.running||this.graph!==o||o.idle()}))().catch(e=>t(this,null,function*(){s()&&(console.error("[artplayerPluginAsr] Initialization failed:",e),yield this.stop())})).finally(()=>{this.starting===n&&(this.starting=void 0)});return this.starting=n,n}tick(e){return t(this,null,function*(){if(!this.running||this.pending)return;const t=this.queue.take(e);if(!t)return;const i=this.epoch,s=this.pending={};try{const e=yield this.options.onAudioChunk(function(t,e){const i=new ArrayBuffer(2*t.length),s=new DataView(i);for(let a=0;a{})),this.closing}}function n(t={}){const{length:e=3,interval:i=100,sampleRate:n=16e3,autoHideTimeout:r=1e4,onAudioChunk:o=()=>null}=t;return t=>{const a=t.layers.add({name:"asr",html:""});if(!a)throw new Error("Could not create ASR subtitle layer");const h=function(t,e,i){let s,n=!1;function r(){clearTimeout(s),s=void 0}function o(){n||(t.style.display="none")}return{append:function(a){n||"string"!==typeof a||(r(),s=setTimeout(o,i),t.style.display="",t.innerHTML=a.split(new RegExp("(?<=[\u3001\u3002\uff01\uff1f!?.])\\s*","u")).map(t=>t.trim()).filter(Boolean).slice(-e).map(t=>`
${t}
`).join(""))},hide:o,destroy(){n=!0,r()}}}(a,e,r),l=new s(t.video,{interval:i,sampleRate:n,onAudioChunk:o},h.append),c=()=>l.start(),d=()=>l.pause(),u=()=>l.restart(),p=()=>(h.destroy(),t.off("play",c),t.off("pause",d),t.off("restart",u),t.off("destroy",p),l.stop(!0));return t.on("play",c),t.on("pause",d),t.on("restart",u),t.on("destroy",p),{name:"artplayerPluginAsr",stop:()=>l.stop(),hide:h.hide,append:h.append}}}if(Object.defineProperty(n,"default",{value:n}),"undefined"!==typeof document&&!document.getElementById("artplayer-plugin-asr")){const t=document.createElement("style");t.id="artplayer-plugin-asr",t.textContent=".art-video-player .art-layer-asr{pointer-events:none!important;position:absolute;z-index:150;inset:0;display:flex;flex-direction:column;justify-content:end;padding:0 2%;font-size:1rem;gap:var(--art-subtitle-gap);padding-bottom:var(--art-subtitle-bottom);transition:padding-bottom var(--art-transition-duration) ease;text-shadow:var(--art-subtitle-border) 1px 0 1px,var(--art-subtitle-border) 0 1px 1px,var(--art-subtitle-border) -1px 0 1px,var(--art-subtitle-border) 0 -1px 1px,var(--art-subtitle-border) 1px 1px 1px,var(--art-subtitle-border) -1px -1px 1px,var(--art-subtitle-border) 1px -1px 1px,var(--art-subtitle-border) -1px 1px 1px}.art-video-player.art-control-show .art-layer-asr{padding-bottom:calc(var(--art-control-height) + var(--art-subtitle-bottom))}",document.head.appendChild(t)}return n}); diff --git a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs index 3da312943..fea4c7923 100644 --- a/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs +++ b/packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs @@ -109,10 +109,6 @@ class AudioGraph { source.connect(gain); gain.connect(context.destination); } - volume(value) { - if (this.gain) - this.gain.gain.value = value; - } disconnect() { if (this.recorder) { this.recorder.port.onmessage = null; @@ -313,9 +309,6 @@ class Capture { this.pending = void 0; } } - volume(value) { - this.graph?.volume(value); - } stop(terminal = false) { this.terminal || (this.terminal = terminal); this.pause(); @@ -364,21 +357,18 @@ function artplayerPluginAsr(option = {}) { throw new Error("Could not create ASR subtitle layer"); const subtitles = createSubtitles(layer, length, autoHideTimeout); const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk }, subtitles.append); - const volume = () => capture.volume(art.volume); const play = () => capture.start(); const pause = () => capture.pause(); const restart = () => capture.restart(); const stop = () => capture.stop(); const destroy = () => { subtitles.destroy(); - art.off("video:volumechange", volume); art.off("play", play); art.off("pause", pause); art.off("restart", restart); art.off("destroy", destroy); return capture.stop(true); }; - art.on("video:volumechange", volume); art.on("play", play); art.on("pause", pause); art.on("restart", restart); diff --git a/packages/artplayer-plugin-asr/src/audio-graph.ts b/packages/artplayer-plugin-asr/src/audio-graph.ts index 2a00d7bab..013fffe5b 100644 --- a/packages/artplayer-plugin-asr/src/audio-graph.ts +++ b/packages/artplayer-plugin-asr/src/audio-graph.ts @@ -111,11 +111,6 @@ export class AudioGraph { gain.connect(context.destination) } - volume(value: number) { - if (this.gain) - this.gain.gain.value = value - } - disconnect() { if (this.recorder) { this.recorder.port.onmessage = null diff --git a/packages/artplayer-plugin-asr/src/capture.ts b/packages/artplayer-plugin-asr/src/capture.ts index e7ac2f3da..19037a0c7 100644 --- a/packages/artplayer-plugin-asr/src/capture.ts +++ b/packages/artplayer-plugin-asr/src/capture.ts @@ -110,10 +110,6 @@ export class Capture { } } - volume(value: number) { - this.graph?.volume(value) - } - stop(terminal = false) { this.terminal ||= terminal this.pause() diff --git a/packages/artplayer-plugin-asr/src/index.ts b/packages/artplayer-plugin-asr/src/index.ts index c51c3be36..fd05f0a84 100644 --- a/packages/artplayer-plugin-asr/src/index.ts +++ b/packages/artplayer-plugin-asr/src/index.ts @@ -12,21 +12,18 @@ export default function artplayerPluginAsr(option: AsrOptions = {}) { throw new Error('Could not create ASR subtitle layer') const subtitles = createSubtitles(layer, length, autoHideTimeout) const capture = new Capture(art.video, { interval, sampleRate, onAudioChunk }, subtitles.append) - const volume = () => capture.volume(art.volume) const play = () => capture.start() const pause = () => capture.pause() const restart = () => capture.restart() const stop = () => capture.stop() const destroy = () => { subtitles.destroy() - art.off('video:volumechange', volume) art.off('play', play) art.off('pause', pause) art.off('restart', restart) art.off('destroy', destroy) return capture.stop(true) } - art.on('video:volumechange', volume) art.on('play', play) art.on('pause', pause) art.on('restart', restart) diff --git a/refactor/baselines/asr-volume-validation.json b/refactor/baselines/asr-volume-validation.json new file mode 100644 index 000000000..4d2a09aa9 --- /dev/null +++ b/refactor/baselines/asr-volume-validation.json @@ -0,0 +1,2549 @@ +{ + "schemaVersion": 1, + "task": "PKG-ASR-07", + "capturedAt": "2026-09-13T07:21:24.743Z", + "baseCommit": "93208e59e14e3384d0a5f5ffa49f55ee95c218c4", + "defect": "Native MediaElementAudioSource already carries HTMLMediaElement volume. Mirroring that value into the ASR playback gain produced squared amplitude.", + "correction": "Remove redundant private volume subscription/forwarders; retain unity playback gain and native media volume/mute.", + "publicSignatureChanged": false, + "audibleBehaviorChanged": true, + "versionChanged": false, + "dependenciesAdded": [], + "red": { + "file": "refactor/.cache/asr07-volume-red/report.json", + "sha256": "151ffb0df8aa4370ee1753ce89c92d4ef53f834fc839dca3ccf0fab1b128055e", + "failed": 1, + "observation": "Unfixed candidate linear-amplitude assertion failed on Chromium." + }, + "browsers": [ + { + "mode": "source", + "file": "refactor/.cache/asr07-browser-source/report.json", + "sha256": "292358cb0348b3ec15da987fac4011c6dac6decd0b3e2ef8baf387ec47152a0d", + "passed": 20, + "skipped": 10, + "rows": [ + { + "title": "ASR 2.0.0 + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "2.0.0", + "core": "published", + "pluginSha256": "30c7b1fc92a262cda8c3aba63afea9c6c038f52c057039d1c6aa957baed155b6", + "integrity": "sha512-nXTf2drMSKnHRbyU9CQ+3o7Jsf1Meam2O9T8LqYakIr18ag/+i71p2x9zdFrLz16Xa6jfQgKeddmU5r3sXp4LA==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.0.0 + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "2.0.0", + "core": "candidate", + "pluginSha256": "30c7b1fc92a262cda8c3aba63afea9c6c038f52c057039d1c6aa957baed155b6", + "integrity": "sha512-nXTf2drMSKnHRbyU9CQ+3o7Jsf1Meam2O9T8LqYakIr18ag/+i71p2x9zdFrLz16Xa6jfQgKeddmU5r3sXp4LA==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "2.1.0", + "core": "published", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "integrity": "sha512-15gXff458PAx9LeJxKzZEvmpFhfwKUfSLgb1Mfr7Xb/ZGsnE+6EA7ezTnO5S55W5mFExjv/e95iOIXP4ijuruQ==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "2.1.0", + "core": "candidate", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "integrity": "sha512-15gXff458PAx9LeJxKzZEvmpFhfwKUfSLgb1Mfr7Xb/ZGsnE+6EA7ezTnO5S55W5mFExjv/e95iOIXP4ijuruQ==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "integrity": null, + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "integrity": null, + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.0.0 + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "2.0.0", + "core": "published", + "pluginSha256": "30c7b1fc92a262cda8c3aba63afea9c6c038f52c057039d1c6aa957baed155b6", + "integrity": "sha512-nXTf2drMSKnHRbyU9CQ+3o7Jsf1Meam2O9T8LqYakIr18ag/+i71p2x9zdFrLz16Xa6jfQgKeddmU5r3sXp4LA==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.0.0 + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "2.0.0", + "core": "candidate", + "pluginSha256": "30c7b1fc92a262cda8c3aba63afea9c6c038f52c057039d1c6aa957baed155b6", + "integrity": "sha512-nXTf2drMSKnHRbyU9CQ+3o7Jsf1Meam2O9T8LqYakIr18ag/+i71p2x9zdFrLz16Xa6jfQgKeddmU5r3sXp4LA==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "2.1.0", + "core": "published", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "integrity": "sha512-15gXff458PAx9LeJxKzZEvmpFhfwKUfSLgb1Mfr7Xb/ZGsnE+6EA7ezTnO5S55W5mFExjv/e95iOIXP4ijuruQ==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "2.1.0", + "core": "candidate", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "integrity": "sha512-15gXff458PAx9LeJxKzZEvmpFhfwKUfSLgb1Mfr7Xb/ZGsnE+6EA7ezTnO5S55W5mFExjv/e95iOIXP4ijuruQ==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "integrity": null, + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "integrity": null, + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR 2.0.0 + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "2.0.0", + "core": "published", + "pluginSha256": "30c7b1fc92a262cda8c3aba63afea9c6c038f52c057039d1c6aa957baed155b6", + "integrity": "sha512-nXTf2drMSKnHRbyU9CQ+3o7Jsf1Meam2O9T8LqYakIr18ag/+i71p2x9zdFrLz16Xa6jfQgKeddmU5r3sXp4LA==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR 2.0.0 + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "2.0.0", + "core": "candidate", + "pluginSha256": "30c7b1fc92a262cda8c3aba63afea9c6c038f52c057039d1c6aa957baed155b6", + "integrity": "sha512-nXTf2drMSKnHRbyU9CQ+3o7Jsf1Meam2O9T8LqYakIr18ag/+i71p2x9zdFrLz16Xa6jfQgKeddmU5r3sXp4LA==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR 2.1.0 + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "2.1.0", + "core": "published", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "integrity": "sha512-15gXff458PAx9LeJxKzZEvmpFhfwKUfSLgb1Mfr7Xb/ZGsnE+6EA7ezTnO5S55W5mFExjv/e95iOIXP4ijuruQ==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR 2.1.0 + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "2.1.0", + "core": "candidate", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "integrity": "sha512-15gXff458PAx9LeJxKzZEvmpFhfwKUfSLgb1Mfr7Xb/ZGsnE+6EA7ezTnO5S55W5mFExjv/e95iOIXP4ijuruQ==", + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "integrity": null, + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "integrity": null, + "candidate": "workspace source", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR 2.1.0 + published: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "2.1.0", + "core": "published", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08787948230355566, + "pcm": [ + 0.08873518745904886, + 0.08885151415070941, + 0.08878124722219483 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.02225400305385114, + "pcm": [ + 0.06267697516034099, + 0.044265941217069706, + 0.04439751512239586 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 0.25, + "rms": 0.0054766057046575665, + "pcm": [ + 0.04414282862893175, + 0.02178400853407414, + 0.021903387849962052 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 0.5, + "rms": 0, + "pcm": [ + 0.021903387849962052, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.02211527189992743, + "pcm": [ + 0.03569403418941531, + 0.04396084522213605, + 0.04395881000500656 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.25323320609674016, + 0.06231950349616454, + 0, + 0.25165455371637563 + ] + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + candidate: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "2.1.0", + "core": "candidate", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08881764562325266, + "pcm": [ + 0.08868048906431901, + 0.08920581671522691, + 0.08873534933928903 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.022029351544008792, + "pcm": [ + 0.06707267117192331, + 0.043581677622483175, + 0.04382086162955032 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 0.25, + "rms": 0.005479147283463036, + "pcm": [ + 0.02965964765401842, + 0.022039641530578833, + 0.022042078815232683 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 0.5, + "rms": 0, + "pcm": [ + 0.022042078815232683, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.021960863247082543, + "pcm": [ + 0.03105405177621801, + 0.04396218678565687, + 0.04394096449693861 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.2480289968217921, + 0.061689850536058154, + 0, + 0.24725788544583013 + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08775032809431582, + "pcm": [ + 0.0883050442445268, + 0.08866511123953921, + 0.08770036081168703 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.044467688262433025, + "pcm": [ + 0.04430890862445003, + 0.04448419659243059, + 0.04456807190430078 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021988159514982068, + "pcm": [ + 0.021858652213692063, + 0.022264040427332873, + 0.02183564101232853 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04445898866431085, + "pcm": [ + 0.04441954293634739, + 0.04399763280919699, + 0.044347005941589086 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.506752387462737, + 0.2505763795133478, + 0, + 0.5066532471140783 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021911062816765935, + "pcm": [ + 0.04441954293634739, + 0.04399763280919699, + 0.044347005941589086 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02207293196472216, + "pcm": [ + 0.021990558616244375, + 0.02196805034152601, + 0.022048608123871345 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08882488738657582, + "pcm": [ + 0.0886315210549575, + 0.08898172845763692, + 0.08914944526890593 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.044017318091028775, + "pcm": [ + 0.04373076063235908, + 0.04454170564933718, + 0.04368522161828582 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021868079293815734, + "pcm": [ + 0.027885735568889247, + 0.022040175688440675, + 0.022023814225070243 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.043786463103167625, + "pcm": [ + 0.0442589307097808, + 0.044428475315698844, + 0.043934021751014976 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.4955516340759375, + 0.24619315528815042, + 0, + 0.4929526441458243 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02209091670307595, + "pcm": [ + 0.0442589307097808, + 0.044428475315698844, + 0.043934021751014976 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022213827390522967, + "pcm": [ + 0.02206083252108882, + 0.02204097726408274, + 0.02206853535288948 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + published: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "2.1.0", + "core": "published", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08911361260496614, + "pcm": [ + 0.08810790258517649, + 0.08855371516543255, + 0.08921661917491973 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.02194707375892373, + "pcm": [ + 0.0887490847119168, + 0.04364493781136172, + 0.04379788626181218 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 0.25, + "rms": 0.005480246246280377, + "pcm": [ + 0.02208047930411219, + 0.022042319293990177, + 0.02202020415953552 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 0.5, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.02178571120620569, + "pcm": [ + 0, + 0.0406408919173163, + 0.04391712633191208 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.2462819441089594, + 0.06149729638471612, + 0, + 0.24447119322588895 + ] + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + candidate: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "2.1.0", + "core": "candidate", + "pluginSha256": "7f975b131a9ea207f88a157aafec778bc4906230500db9b30f5bbfbf327b251d", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08894149183377073, + "pcm": [ + 0.08806569795731894, + 0.08906136820059894, + 0.08881292144959071 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.02199682193233565, + "pcm": [ + 0.04364857262658239, + 0.043870321337387476, + 0.044029452353622496 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 0.25, + "rms": 0.005479538204162045, + "pcm": [ + 0.029019733025840003, + 0.02207914167636577, + 0.02203781349584488 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 0.5, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 0.5, + "rms": 0.021924296675467796, + "pcm": [ + 0, + 0.034459398843484236, + 0.04444793222890891 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.24731788818482067, + 0.061608346016987836, + 0, + 0.24650246160075345 + ] + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08844675881791897, + "pcm": [ + 0.08908175741400112, + 0.08764625829213239, + 0.08868106763078791 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04470897006898172, + "pcm": [ + 0.044736434210732895, + 0.04429639255821901, + 0.04427936387911991 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021938261219403916, + "pcm": [ + 0.021897200963776692, + 0.02197702951090593, + 0.02199273705801606 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04423520425706149, + "pcm": [ + 0.04411263044971351, + 0.04414790888442041, + 0.04427520692950356 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.5054902029934403, + 0.2480391764786672, + 0, + 0.5001336945328472 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021835679200481448, + "pcm": [ + 0.04411263044971351, + 0.04414790888442041, + 0.04427520692950356 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02199648638366717, + "pcm": [ + 0.021859364677423183, + 0.021978348219921374, + 0.0219627303115079 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "32d56ea2c9c59106ee390b129e90592d3e60f5060f5f31b7cc54faaf17563273", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08822370735982864, + "pcm": [ + 0.08785772823558756, + 0.0886982476071749, + 0.08835307958400736 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.044526577249936555, + "pcm": [ + 0.044462034351064085, + 0.04452233848188662, + 0.044370191962241144 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021958017689125017, + "pcm": [ + 0.021816397151351745, + 0.02202977594790184, + 0.021927973574636505 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04417134732722049, + "pcm": [ + 0.04426830650564946, + 0.04408435633999407, + 0.044130494357758614 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.5047008177556034, + 0.24889021722434754, + 0, + 0.5006743498894637 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02196032797045319, + "pcm": [ + 0.04408435633999407, + 0.044130494357758614, + 0.044341377777106714 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022105731383390018, + "pcm": [ + 0.021902752505399848, + 0.02194767866111349, + 0.022039335220178258 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR 2.1.0 + published: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR 2.1.0 + candidate: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + } + ] + }, + { + "mode": "main", + "file": "refactor/.cache/asr07-browser-main/report.json", + "sha256": "36166874f2e42a50715726d9f72e11aa729e3ceae1737018e00cbb2ad2a086b0", + "passed": 8, + "skipped": 4, + "rows": [ + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08898306406531939, + "pcm": [ + 0.0894328088150841, + 0.08866579205858138, + 0.08853906133305224 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04392166999635796, + "pcm": [ + 0.0438102006301307, + 0.04395067395512343, + 0.043990099165068054 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021916953534556336, + "pcm": [ + 0.021977743831376403, + 0.02211544997380876, + 0.021961727298633844 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.043763810629487906, + "pcm": [ + 0.04421968057851872, + 0.04449294235554753, + 0.04409959886837273 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.4935958371147635, + 0.2463047745632569, + 0, + 0.4918217987791745 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022082581769601393, + "pcm": [ + 0.04421968057851872, + 0.04449294235554753, + 0.04409959886837273 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02204817145046479, + "pcm": [ + 0.022001679392206895, + 0.022108533823453156, + 0.021893407589070157 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08899373366704011, + "pcm": [ + 0.0894328088150841, + 0.08866579205858138, + 0.08853906133305224 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04394683524153309, + "pcm": [ + 0.0438102006301307, + 0.04395067395512343, + 0.043990099165068054 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02189275439043324, + "pcm": [ + 0.022179777958503403, + 0.021977743831376403, + 0.02211544997380876 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04374915659941184, + "pcm": [ + 0.04429160160247414, + 0.04436201149291704, + 0.04420321607182133 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.4938194345902505, + 0.24600332504693512, + 0, + 0.491598169856479 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022022294040289086, + "pcm": [ + 0.04429160160247414, + 0.04436201149291704, + 0.04420321607182133 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02221016735587576, + "pcm": [ + 0.02222397189292957, + 0.022056087711408684, + 0.022101736879748 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08845187451056867, + "pcm": [ + 0.08908183979042286, + 0.08764581308134357, + 0.08868078531214164 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04474128178266116, + "pcm": [ + 0.04473653542507642, + 0.044296986412714734, + 0.04427968413643314 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02193476969950652, + "pcm": [ + 0.021898050861181014, + 0.021977304642445614, + 0.021992905994729868 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.044193239085988746, + "pcm": [ + 0.04411792860634942, + 0.04414790888442041, + 0.04427520692950356 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.5058262702766717, + 0.24798535724514967, + 0, + 0.4996303281363282 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021854309327144418, + "pcm": [ + 0.04411792860634942, + 0.04414790888442041, + 0.04427520692950356 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022089585292791467, + "pcm": [ + 0.022122731888305103, + 0.022002207752029344, + 0.022103515452374583 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08845187451056867, + "pcm": [ + 0.08908183979042286, + 0.08764581308134357, + 0.08868078531214164 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04474128178266116, + "pcm": [ + 0.04473653542507642, + 0.044296986412714734, + 0.04427968413643314 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02193476969950652, + "pcm": [ + 0.021898050861181014, + 0.021977304642445614, + 0.021992905994729868 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.044193239085988746, + "pcm": [ + 0.04411792860634942, + 0.04414790888442041, + 0.04427520692950356 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.5058262702766717, + 0.24798535724514967, + 0, + 0.4996303281363282 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.0219016020765604, + "pcm": [ + 0.04411792860634942, + 0.04414790888442041, + 0.04427520692950356 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022002062309001577, + "pcm": [ + 0.022122731888305103, + 0.022002207752029344, + 0.022103515452374583 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + } + ] + }, + { + "mode": "legacy", + "file": "refactor/.cache/asr07-browser-legacy/report.json", + "sha256": "40bc10e1ca4a8ad2bdf55592793163cd3507441e1d6249927a5cdb7416fa0f4f", + "passed": 8, + "skipped": 4, + "rows": [ + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native Worklet supplies local PCM with owned stop and destroy", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "integrity": null, + "candidate": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "media": "local mono AAC 440 Hz tone", + "networkService": false + }, + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit 26.6 has no AudioContext/AudioWorkletNode; native ASR acceptance remains open", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-audio.spec.js", + "line": 30, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08899373366704011, + "pcm": [ + 0.0894328088150841, + 0.08866579205858138, + 0.08853906133305224 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.043976319029964135, + "pcm": [ + 0.0438102006301307, + 0.04395067395512343, + 0.043990099165068054 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02192187220893584, + "pcm": [ + 0.021977743831376403, + 0.02211544997380876, + 0.021961727298633844 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04366204553679634, + "pcm": [ + 0.04429160160247414, + 0.04436201149291704, + 0.04420321607182133 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.4941507364383262, + 0.24633051458380228, + 0, + 0.49061932495329275 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022028915495856324, + "pcm": [ + 0.04429160160247414, + 0.04436201149291704, + 0.04420321607182133 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022045989687634857, + "pcm": [ + 0.022000040976758138, + 0.02217817019274153, + 0.021921455798160818 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "chromium", + "status": "passed", + "browser": "153.0.8010.12", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08895661414135936, + "pcm": [ + 0.08863350568420335, + 0.08920581671522691, + 0.08877002180995251 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04393690088059405, + "pcm": [ + 0.05747055502473811, + 0.043647279661655436, + 0.044224148744452615 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02197209978139949, + "pcm": [ + 0.02211387667166409, + 0.02202662888402955, + 0.022082609916615908 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04370582206691088, + "pcm": [ + 0.04093501592551867, + 0.044236828392531444, + 0.044431490835269934 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.49391381747932434, + 0.24699793257063515, + 0, + 0.4913161600042324 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022050215264175072, + "pcm": [ + 0.04093501592551867, + 0.044236828392531444, + 0.044431490835269934 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022220550514953648, + "pcm": [ + 0.022157656047430068, + 0.022192578857802337, + 0.022111267555499407 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "published", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.0879359081368704, + "pcm": [ + 0.08806340737359523, + 0.08878631920248814, + 0.08781769492685394 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.044486355327500546, + "pcm": [ + 0.04426667504059511, + 0.04457071721526771, + 0.044458322128512924 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021930250598246468, + "pcm": [ + 0.02178605879790252, + 0.022265169163584512, + 0.021818966937776837 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.04418653523700871, + "pcm": [ + 0.044110437327030495, + 0.04438105335280501, + 0.04385757054102199 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.5058952169830152, + 0.24938902733696106, + 0, + 0.5024856872829845 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02194657887986621, + "pcm": [ + 0.04438105335280501, + 0.04385757054102199, + 0.044387279046952124 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.02222177826587658, + "pcm": [ + 0.022102682896685707, + 0.02199731041007729, + 0.022145763195605957 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "firefox", + "status": "passed", + "browser": "155.0", + "inputs": { + "version": "candidate", + "core": "candidate", + "pluginSha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "mediaSha256": "83e6672b880b5d1c97093b1e26bdc40221c0b279615f40e99dbf9d2ade7b4d56", + "meter": "Native unity analyser inserted between plugin gain and destination", + "physicalAudioOutput": false + }, + "output": { + "levels": [ + { + "volume": 1, + "muted": false, + "gain": 1, + "rms": 0.08790570781822375, + "pcm": [ + 0.08764581308134357, + 0.08868078531214164, + 0.08832207248279998 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.044559559797946756, + "pcm": [ + 0.04473653542507642, + 0.044296986412714734, + 0.04427968413643314 + ] + }, + { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021929780648391274, + "pcm": [ + 0.021898050861181014, + 0.021977304642445614, + 0.021992905994729868 + ] + }, + { + "volume": 0.5, + "muted": true, + "gain": 1, + "rms": 0, + "pcm": [ + 0, + 0, + 0 + ] + }, + { + "volume": 0.5, + "muted": false, + "gain": 1, + "rms": 0.0442728102165753, + "pcm": [ + 0.04414790888442041, + 0.04427520692950356, + 0.04411625608131577 + ] + } + ], + "relativeAmplitude": [ + 1, + 0.5069017803723219, + 0.24946935975692133, + 0, + 0.5036397671482841 + ], + "restart": { + "stopped": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.021901460587205615, + "pcm": [ + 0.04414790888442041, + 0.04427520692950356, + 0.04411625608131577 + ] + }, + "resumed": { + "volume": 0.25, + "muted": false, + "gain": 1, + "rms": 0.022164848591564024, + "pcm": [ + 0.022102102897757048, + 0.021997384739638595, + 0.022145946735187876 + ] + } + } + }, + "annotations": [] + }, + { + "title": "ASR candidate + published: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + }, + { + "title": "ASR candidate + candidate: native playback volume and mute", + "project": "webkit", + "status": "skipped", + "browser": "26.6", + "annotations": [ + { + "type": "skip", + "description": "Windows WebKit lacks native AudioContext/AudioWorkletNode", + "location": { + "file": "D:\\github\\ArtPlayer\\test\\browser\\asr-playback.spec.js", + "line": 24, + "column": 14 + } + } + ] + } + ] + } + ], + "artifacts": [ + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.js", + "sha256": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "bytes": 8700, + "docsCopyIdentical": true + }, + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.legacy.js", + "sha256": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "bytes": 9177, + "docsCopyIdentical": true + }, + { + "file": "packages/artplayer-plugin-asr/dist/artplayer-plugin-asr.mjs", + "sha256": "da252aca75ae27dd8a1c752d8c862100d832cc4312fe504e57c54b5a36a51b3a", + "bytes": 13041, + "docsCopyIdentical": true + } + ], + "unit": { + "focused": 208, + "repeatedMain": 25, + "repeatedLegacy": 25 + }, + "installed": { + "file": "refactor/.cache/asr-package-types-e4kiFk/report.json", + "sha256": "050648c53b5f8f34f3ff538e4feaf3dc3df47f0c9ec3628316919f1bcabf698e", + "cases": 17, + "candidateConfigurations": 7, + "rejectedUsesPerCandidate": 12, + "packages": [ + { + "name": "artplayer", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\asr-package-types-e4kiFk\\artplayer.tgz", + "sha256": "0c0ea52d41575261b836aefac68280733b119c75834ecad6d4bc78edb073f4c1", + "files": { + "package/ARCHITECTURE.md": "bee98fc9d93a3911d58e98ef15f2dbe07e12f2274c444d989094248a22079e46", + "package/README.md": "1e185e65f67fc2081b4182bbc6c4558ea05b6e3fa3acc5dd0c3ac7df1d542b09", + "package/THIRD_PARTY_NOTICES": "b77b5b46b79fa0cda850b0781f78c3b1112c4ed9672fa291956fe8eff53098e7", + "package/dist/artplayer.js": "6dc8ca6d6eb34aa8a056cbc31328efd634530b3c5c60f0014cb7ce719fabc335", + "package/dist/artplayer.legacy.js": "80f74a8a39df4baf64cc00c65d21cd57c011bfcdead0da38eea077fbc67c5c04", + "package/dist/artplayer.mjs": "432e2b0ad92009a09743e020bf7c928c8b8f18a52496622b16259ec9ab20059f", + "package/dist/i18n/ar.js": "bd0bef65f4f7dc3f8ed562904cc6fa9071ff4537fb64c1e0fc3d554141bcd3cb", + "package/dist/i18n/ar.mjs": "59310b7bbeea83bbb7d73aadc5ca82a6ec269c3530d19aa741c94d5c64fc5da8", + "package/dist/i18n/cs.js": "6e2c93daba24c2e48dfa206bcf4f2f2a8a02713b856e404d2e4e4ecd8309ca2f", + "package/dist/i18n/cs.mjs": "c73fba18797d62d7b2581e67fe7340c35fe84510b2250ff1ecaad253d9cabbac", + "package/dist/i18n/es.js": "f4133ac6ea06b69a474b33c4a1809a4066e2376bb9464a3f3aa8330f01f1944a", + "package/dist/i18n/es.mjs": "1390bca3def2be115e811de337df0a0c3f63998e51359f283023246245c552a7", + "package/dist/i18n/fa.js": "8d3ef2e0f0cc0ea480c2167b6fcb87cdf444e90759c3519e8daa139fec576da4", + "package/dist/i18n/fa.mjs": "ab441e8a2d79043b29ceca87c783bf06874bb9d3a149c1604bc167eb481be2a9", + "package/dist/i18n/fr.js": "89dc4194a6c0a962b9ce756b52c90401c1acf3b159f265f3bc0d7d6d7d987f03", + "package/dist/i18n/fr.mjs": "99491878430fb7b6f2b7bf93da0588a831704b412e01116f23ba1d9d45863816", + "package/dist/i18n/id.js": "4c6dc86db741bcc2857df55a9d037e82e91d375fa6fd22e0562809ff621b92db", + "package/dist/i18n/id.mjs": "383e5803a6b6686c6de4e867770241de75b6f517dcca2bdedc6f2004bf6e1fb3", + "package/dist/i18n/pl.js": "3a38828b1c3d719c0aca9906cf6a46c23555a8411f8d57645f0e0ce8147dc664", + "package/dist/i18n/pl.mjs": "079ed96732979035297af387c64a7b99519b5a43107fb86735a654ea4fe8e9b5", + "package/dist/i18n/ru.js": "62fac9dddb302eb16382e6e9301fc4b2c66f5f69d4a358a26b67026a5fa4e787", + "package/dist/i18n/ru.mjs": "a893aeaee1d79409db72e324fbff200e3864f33ed168f04d91d25f7cf2efa765", + "package/dist/i18n/tr.js": "1492c36b069a7d73db4f3913bd18085faff7afd8b3c158138fe264e31600e38c", + "package/dist/i18n/tr.mjs": "a90478cac078836b76c2a98261907610fee07f3e4b28671b8e7dbf533778f918", + "package/dist/i18n/vi.js": "c7216ef7e608a79b7e2c479a07bf310d4ab79577947ac4a62d676faaced0e9cf", + "package/dist/i18n/vi.mjs": "66270fdfa0b5da396322d1769169b7df37a030ba15ac18eb30bd87b3af8074ec", + "package/dist/i18n/zh-tw.js": "11c49857d6d40a5ffd224ffa59c9026e71621b345f63c5a086326db80a22371a", + "package/dist/i18n/zh-tw.mjs": "a38e630cb96de96c15d824686aec750e25cf6c8ada01285a292442a81566314f", + "package/package.json": "82e92699e7c29847f32ac6767ece0401290e7d7a12854722bdbcfd09b90b7954", + "package/types/COMPATIBILITY.md": "a7da9e102a116104b513df4f7451247b7759bc502d3375f30bd5b14a541826e0", + "package/types/README.md": "1bd9868e773610e6312fd48a1c5aa20772d29df0af40c3db66e75b7ab4a1aee0", + "package/types/artplayer.d.cts": "3685065e4263abcfd9d953176e461d5b3eb990b0363b54689a7f55deba770fef", + "package/types/artplayer.d.mts": "b664fec51ee5c5381f2ceab25d99d52aee56feab04a2ee7b4ea5b2145ea5fa0e", + "package/types/artplayer.d.ts": "5d0162adf7ac6d0f2bb9b5cfe361f28657c4d9c540cadbf36eba3cc954ce50f8", + "package/types/component.d.ts": "ae5bc1ddeaa1ee1ebbc62573e32423993727079205c64751a5a314170e588a32", + "package/types/config.d.ts": "36f9a84cd436f17701a0d11d3417fc1fe0414e0c52c80083b94a85dfc329e37e", + "package/types/cssVar.d.ts": "93217860fff2ebf750f606d95ddd22220a58837119e63bdf2f5f78fd7488458b", + "package/types/emitter.d.ts": "7e1f9a2a8ce65feb95734f1ed52d1334e57aa388bbef2a43d8bc53a9b6f6623c", + "package/types/events.d.ts": "f83624c9268a284477e8bd793fe3562a2363b72f2a44d5c64fe8c6a6cede8830", + "package/types/i18n.d.ts": "f4f2f05bdfda73c74fef34767cb18ee8e11b743120b1c4598e9714d7846018e5", + "package/types/icons.d.ts": "326a67ba7bdcf52862c5b2a269e540140aff98d05c7b96196485a77f84fe5960", + "package/types/language.d.cts": "f7928e108cf7b7814c2d75cb0a525333e95873fca6cd6d89a740450656b9c1cd", + "package/types/language.d.mts": "6619a26d604b7cccbe2f596243d87ae37cfaae33b8b52874ec071d9787446f9e", + "package/types/legacy/language.d.ts": "c7abda4d506fd933c350e9030290bf9e64347a921d683a66115da1995ebe0a81", + "package/types/option.d.ts": "ccf2c8ddc537dc65c9682d39f044726e23bff18f476b622512d7e6a4b949b5f6", + "package/types/playback.d.ts": "58705c273df6e76a77a6575e769a9ec6c013785c25080f2551aa22415788d870", + "package/types/player.d.ts": "92b00211b70ac71336e495899df972a0519dd138111846044fe4e0f967966177", + "package/types/plugin.d.ts": "fc3594a41c51d8248713e33c486fb1f6a518ac5c7ce31be6988a6cd8d052c5f3", + "package/types/quality.d.ts": "1ac1a48b4f74ff9ba1ce60443e1463d50d1c6a2e5d87d63dc7d2a8eb7f4c2c34", + "package/types/runtime.d.cts": "7ee988dfea9a0ac71a34a656f96f7b74e78aa92547cfdfab7ff7e1139a77d4a3", + "package/types/runtime.d.mts": "dcb85f59a8b7714918ced0c1d18c2bb24e8b80b56d8c594e019dcbdb5ad88b08", + "package/types/runtime.d.ts": "9c6ccd39a1be293f17ae46dae8ea077053e12635ee79d73a5e3b3a559230fcff", + "package/types/runtime/component.d.ts": "1b4c0cee44568649e80140fe778febfcae88af0ac85515f1cbeff78ab0c6d14f", + "package/types/runtime/construction.d.ts": "7a87bedf6f7ffff0c46078aa5f1551e0077d35574e286516121f5f935e2d9ce4", + "package/types/runtime/events.d.ts": "a1e2f9d387475453eaf95c2a5ee79e228636c0bb859bda3fcc46da46574a0c31", + "package/types/runtime/media.d.ts": "8fc89401be7fe25542c169d07dd5675f10916ed20be8493e19695966cf3864a8", + "package/types/runtime/option.d.ts": "6e334e1a76fdcc40b4665c02523bd31c3994d96078fadeb40e23422aa54c1a1d", + "package/types/runtime/player.d.ts": "8ac6383502e4872e83659838844856903031a81a0adc8e8535feb78453aa042c", + "package/types/runtime/plugin.d.ts": "8528f2b17843149f42f9cc16d6fd70baa238c9dc2c329b67d2c38096799c69bd", + "package/types/runtime/services.d.ts": "604c9f8e4b6c0da818f96576bf9f4b0e96085a9003927b2f7ba30027b1ba237e", + "package/types/runtime/setting.d.ts": "349c1fcb205ad312916bd3ee55e6bf241ea8a569fb2ab6c80822e69fc518f7d7", + "package/types/runtime/subtitle.d.ts": "269ff84678d327b3a9dc2f5bc6cc0d731d1e081b85422e474621d5d792efb153", + "package/types/runtime/template.d.ts": "7a62f968a38b4bf9109a4439557c02074eb1d62ba1f6e21a8eb194e91f0badfe", + "package/types/runtime/utils.d.ts": "6bf731705d86853e066dbaf3b8d4003b6548423fe615b7412b722bd0d3a6e48b", + "package/types/setting.d.ts": "08305a75dff96f06490430e099338b463df471b550c1d0dac3abc8995b39eb09", + "package/types/subtitle.d.ts": "b18314e2d64efdb6045830b6e12f615ae2ed585416130e562329ad3ea337f6ce", + "package/types/template.d.ts": "4792bc0c2cbcb7966e4375118bd34b326166e0831862e94ace4cf6b1bbe6a8c8", + "package/types/utils.d.ts": "fca0d3d814df94a25f9eb067c86fa0cf272e2791bac793b91229d57680797712" + } + }, + { + "name": "artplayer-plugin-asr", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\asr-package-types-e4kiFk\\artplayer-plugin-asr.tgz", + "sha256": "10e7f751d974f9967c1008957d9babdf59c35f3b91c66576334a7f8e21ca623f", + "files": { + "package/ARCHITECTURE.md": "a46a1abf28a4c783a4cec2983c4ba276b7e9e33e8446d811cc9c49c1f5231ef8", + "package/README.md": "d8eec6fafd308a3f52c16548a27ea07c2cb247d950b3e155d94f98e010e6b994", + "package/dist/artplayer-plugin-asr.js": "5f66c215c781f8e539ee56bc680f7870484608d98e773ce792412b513395eeb5", + "package/dist/artplayer-plugin-asr.legacy.js": "ff5b676153d9805e6610fdbdcc22d6de2bc3f2f5082363a6828c60488ce0821a", + "package/dist/artplayer-plugin-asr.mjs": "da252aca75ae27dd8a1c752d8c862100d832cc4312fe504e57c54b5a36a51b3a", + "package/package.json": "781b1eebefdff5fdc234b793ff9d6d8263275115ccff4c4d90c55eca942f2444", + "package/types/artplayer-plugin-asr.d.ts": "8fce55a45c368dc95fc6169750d2157ad57f683f62b586b8162ac720e1efdf42", + "package/types/runtime.d.cts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606", + "package/types/runtime.d.mts": "ff0769d3e408d66c7d38574171ff2621456afccc50ee8191ca4ee394dc57666e", + "package/types/runtime.d.ts": "2965dccc49a3a75c3b16c0c13501077b8ac199e78bac2d19d57be3d811583606" + } + } + ] + }, + "logs": [ + { + "file": "refactor/.cache/asr07-build.log", + "sha256": "efd177a81dd234a357c6411414a8befee3a1df75c4ffc536548cc4721c6f6c7d" + }, + { + "file": "refactor/.cache/asr07-lint.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "refactor/.cache/asr07-strict.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "file": "refactor/.cache/asr07-unit.log", + "sha256": "16645cb72090db2572398d0b3a1ec2952adae675ec5a577e25b88175d069e6ff" + }, + { + "file": "refactor/.cache/asr07-unit-main.log", + "sha256": "c969345b63033e8a3e019e37c17925ce0e56deb1bd4299e2667702d7917dc5d2" + }, + { + "file": "refactor/.cache/asr07-unit-legacy.log", + "sha256": "0d5c21fbbc398cd78d51f260103c22c3bf2e03173f4e5f613f923ac08af121bd" + }, + { + "file": "refactor/.cache/asr07-installed.log", + "sha256": "77749e6ed3234f748e5e2b202a0e8c38a3b59b4b951064456d4c4df8e03c309e" + } + ], + "inputs": { + "packages/artplayer-plugin-asr/src/index.ts": "e8c14622919878f472b4168cf5719fe457673eb11d0eb15f4a5703d38fca3df0", + "packages/artplayer-plugin-asr/src/capture.ts": "15580482978be74c933a4e9b1ce447b7b317bb1bfd14171def91882e375e239a", + "packages/artplayer-plugin-asr/src/audio-graph.ts": "2d5f63d6ebdfb9d535b806d21a69bdbd669c7bab3cc42f8f74c54f19f02f1aba", + "test/asr-lifecycle.test.js": "b4cff011fe16f1ad3e0cdc3aa92a9ee121b5af3f351cefb8b6e72de8e60d47ee", + "test/browser/asr-playback.spec.js": "3d02db0d1070b1732d8595883a64475473ab1021dc0f77cfdedfd86889d9f794", + "test/browser/asr-audio.spec.js": "c61df3d1a4db54083ac75d5f2832afa40085a6fb8a9e0d6cc7205d1fbb3898c8" + }, + "limitation": [ + "Browser analyser route is not physical speaker/device output.", + "Windows WebKit skips do not establish Safari coverage.", + "Captured fallback topology, cross-origin media and audio-track combination remain ASR-05.", + "No actual npm release 5.3.1 exists; source association is not a published baseline.", + "No full CI rerun; no publish." + ], + "releaseReady": false +} diff --git a/refactor/changes/2026-09-13-PKG-ASR-07-playback-volume.md b/refactor/changes/2026-09-13-PKG-ASR-07-playback-volume.md new file mode 100644 index 000000000..b6cfc7804 --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-ASR-07-playback-volume.md @@ -0,0 +1,39 @@ +# PKG-ASR-07:修复 ASR 音量二次衰减 + +ASR-05 的原生验证发现,2.1.0 和 ASR-04 候选都在 `video:volumechange` 中把 +`art.volume` 再写入播放 gain。原生 MediaElementAudioSource 已包含视频音量, +二次应用导致 50% 设置约为 25% 输出振幅,25% 设置约为 6.25%。Chromium 和 Firefox +均复现。此缺陷单独增加任务与风险记录,ASR-05 的完整设备验收继续独立跟踪。 + +删除重复的 volume 监听器及 capture/graph 转发方法,内部播放 gain 保持 1。 +视频继续拥有音量/静音控制。公开选项、工厂、回调、返回值和停止/销毁语义不变; +这是输出音量的行为修复,升级后调过音量的播放可能比旧版更响,不应声称声音字节 +完全不变。PCM 仍随主视频音量缩放,静音时为零,不自动改为捕获外部音轨。 + +## 实测方法与验证 + +`test/browser/asr-playback.spec.js` 在原生 gain 与 destination 之间插入 unity +AnalyserNode,以同一本地 AAC 的全音量 RMS 归一化。设置 1/0.5/0.25、静音和取消静音; +候选允许 10% 窗口处理误差,要求线性振幅,旧发布版要求其原有平方衰减。 +候选另检查 stop 后音量变化仍正确、采集停止、pause/play 后恢复正确输出。 +该方法检查浏览器图信号,不代替物理扬声器或声卡测量。 + +修复前候选 Chromium 线性断言失败,报告和 trace 保留。修复后: + +- 208 项定向 ASR 单元/分发测试通过,main/legacy 各 25 项生命周期通过。 +- source 原生组 20 通过/10 跳过;main、legacy 各 8 通过/4 跳过。合计 + 36 通过、18 明确的 Windows WebKit 无 WebAudio 跳过,0 failed/flaky。 +- 包含发布核心 5.4.0 和候选核心,原生 PCM/WAV、字幕、暂停、切源、stop/restart、 + context 最终关闭及音量验证。来源提交的核心 5.3.1 未发布到 npm,不能伪造其发布 + 基线;相邻稳定 5.3.0 对照与物理设备仍由 ASR-05 继续取证。 +- 正常三格式构建、docs 副本字节、分包 strict、定向 lint 通过。 + 更新产物重新隔离安装,17 配置声明/真实导出矩阵通过;原有 NodeNext 类型诊断 + 继续明确保留。没有重复整仓 CI,没有增加依赖或改版本。 + +输入哈希、红绿报告和包安装摘要见 +[音量验证记录](../baselines/asr-volume-validation.json)。本次新产物取代 ASR-03/04 +记录的候选哈希;旧证据保留为对应历史提交的观察,不自动宣称覆盖新产物。 + +本任务不关闭捕获流 fallback 的原生拓扑、CORS、audio-track 组合或 Safari/设备 +门槛。回退本提交会恢复已复现的音量二次衰减。子代理同期生成的离线 demo 与旧核心 +取证属于 ASR-05 后续工作,不混入本项完成声明。 diff --git a/refactor/plan.md b/refactor/plan.md index 21dfbca44..e8bc5b918 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -2,9 +2,9 @@ > 由 tasks.json 生成。请修改数据后运行 `node refactor/scripts/plan.mjs --write`,不要手改本表。 -基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 225 项,范围 22 个包及工作区/示例。 +基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 226 项,范围 22 个包及工作区/示例。 -状态:todo 82 / doing 16 / blocked 0 / done 127 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 82 / doing 16 / blocked 0 / done 128 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -17,7 +17,7 @@ | artplayer | 5.4.1 | CORE-01, CORE-02, CORE-03, CORE-04, CORE-05, CORE-06, CORE-07, CORE-08, CORE-09, CORE-10, CORE-11, CORE-12, CORE-13, CORE-14, CORE-15, CORE-16, CORE-17, CORE-18, CORE-19, CORE-20, CORE-21, CORE-23, CORE-22, CORE-24, CORE-25, ENG-12 | | artplayer-plugin-ads | 2.1.0 | PKG-ADS-01, PKG-ADS-02, PKG-ADS-03, PKG-ADS-04, PKG-ADS-05, PKG-ADS-06 | | artplayer-plugin-ambilight | 1.1.0 | PKG-AMBILIGHT-01, PKG-AMBILIGHT-02, PKG-AMBILIGHT-03, PKG-AMBILIGHT-04, PKG-AMBILIGHT-PROXY-01, PKG-AMBILIGHT-05, PKG-AMBILIGHT-06, PKG-FACTORY-01 | -| artplayer-plugin-asr | 2.1.0 | PKG-ASR-01, PKG-ASR-02, PKG-ASR-03, PKG-ASR-04, PKG-ASR-05, PKG-ASR-06 | +| artplayer-plugin-asr | 2.1.0 | PKG-ASR-01, PKG-ASR-02, PKG-ASR-03, PKG-ASR-04, PKG-ASR-05, PKG-ASR-06, PKG-ASR-07 | | artplayer-plugin-audio-track | 1.1.0 | PKG-AUDIO-01, PKG-AUDIO-02, PKG-AUDIO-03, PKG-AUDIO-04, PKG-AUDIO-05, PKG-AUDIO-06 | | artplayer-plugin-auto-thumbnail | 1.1.0 | PKG-AUTO-THUMB-01, PKG-AUTO-THUMB-02, PKG-AUTO-THUMB-03, PKG-AUTO-THUMB-04, PKG-AUTO-THUMB-05, PKG-AUTO-THUMB-06 | | artplayer-plugin-chapter | 1.1.0 | PILOT-01, PKG-CHAPTER-01, PKG-CHAPTER-02, PKG-CHAPTER-03, PKG-CHAPTER-04, PKG-CHAPTER-05, PKG-CHAPTER-06 | @@ -255,8 +255,9 @@ | PKG-ASR-02 | artplayer-plugin-asr
建立特有行为与错误测试 | PKG-ASR-01, ENG-03, ENG-05 | PCM/WAV、chunk 时序、回调慢/拒绝、重复初始化与停止 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done | | PKG-ASR-03 | artplayer-plugin-asr
整理内部职责与资源 | PKG-ASR-02, CORE-10, CORE-18 | AudioContext/Worklet/Stream 生命周期和背压分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done | | PKG-ASR-04 | artplayer-plugin-asr
迁移自有源码和公开类型 | PKG-ASR-03, ENG-04, ENG-06, CORE-07 | 主线程/Worklet/音频 buffer 与异步回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done | -| PKG-ASR-05 | artplayer-plugin-asr
验证新旧核心和组合 | PKG-ASR-04, CORE-22 | 真实 WebAudio 分块、播放暂停/切源/销毁,不引入网络 ASR | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | +| PKG-ASR-05 | artplayer-plugin-asr
验证新旧核心和组合 | PKG-ASR-04, CORE-22, PKG-ASR-07 | 真实 WebAudio 分块、播放暂停/切源/销毁,不引入网络 ASR | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | | PKG-ASR-06 | artplayer-plugin-asr
验证分发并同步文档 | PKG-ASR-05, ENG-07 | asr.js、Worklet 资源、输出格式与声明证据 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | +| PKG-ASR-07 | artplayer-plugin-asr
修复原生播放音量二次衰减 | PKG-ASR-04 | 移除重复音量应用;保留新旧原生音频振幅对照及正式产物验收 | Chromium/Firefox 两核心和 source/main/legacy 输出音量线性、静音和 stop/restart 正确;旧版平方衰减单独保留;单元/类型/真实安装通过,不代替设备验收 | H | done | ## 5 包迁移:artplayer-plugin-ads @@ -568,3 +569,4 @@ - PKG-FACTORY-01: [记录](baselines/factory-assignment-gaps.json) [记录](baselines/factory-compatibility-proposals.json) [记录](factory-compatibility-decision.md) [记录](changes/2026-09-12-PKG-FACTORY-01-decision.md) - CORE-25: [记录](changes/2026-09-13-CORE-25-defaults-ssr.md) [记录](baselines/defaults-ssr-validation.json) - ENG-12: [记录](changes/2026-09-13-ENG-12-library-public.md) [记录](baselines/library-public-validation.json) +- PKG-ASR-07: [记录](changes/2026-09-13-PKG-ASR-07-playback-volume.md) [记录](baselines/asr-volume-validation.json) diff --git a/refactor/progress.md b/refactor/progress.md index 92811e1d6..7a93db30d 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,18 @@ # 进度与证据 +## PKG-ASR-07 原生音量修复完成 + +ASR-05 原生验证发现旧版与候选都重复应用音量:50% 设置只有约25%输出振幅。 +新增独立修复任务,移除重复监听/转发,保留媒体原生音量与静音。修复前线性断言 +已在 Chromium 失败;修复后 source/main/legacy 两核心验证共36过、18明确能力跳过, +涵盖原有采集生命周期和音量/静音/stop后播放与恢复。208定向及两产物各25生命周期 +通过,正常构建、strict/lint和重新隔离安装17配置通过。见[变更](changes/2026-09-13-PKG-ASR-07-playback-volume.md) +与[证据](baselines/asr-volume-validation.json)。 +226项:128 done、16 doing、82 todo;206风险。旧版音量缺陷已修复;物理设备、fallback、 +跨源和插件组合仍属 ASR-05。子代理已准备离线示例,并确认来源核心5.3.1未发布; +后续以真实相邻稳定5.3.0作扩大对照,不能伪造发布基线。无全量CI、推送或发布。 + + ## PKG-ASR-04 公共类型与安装兼容完成 保留根入口及 legacy 的旧工厂、void stop 和 NodeNext ESM 命名空间形状;新增 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 5bce4e423..ec3593c27 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -209,3 +209,4 @@ | ASR-TYPE-01 | resolved / 已复现 | ASR declarations omit asynchronous stop and callback text results | PKG-ASR-04 | | ASR-CHUNK-01 | resolved / 已复现 | ASR tick discards underfilled chunks and overrun samples in source | PKG-ASR-02, PKG-ASR-03 | | ASR-LIFE-01 | resolved / 已复现 | ASR partial initialization, pause resume and stale callback cleanup need controlled evidence | PKG-ASR-02, PKG-ASR-03, PKG-ASR-05 | +| ASR-VOLUME-01 | resolved / 已复现 | ASR applies media volume twice and squares playback amplitude | PKG-ASR-07 | diff --git a/refactor/risks.json b/refactor/risks.json index 767559716..6ff0fd661 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -4617,6 +4617,26 @@ "test/asr-encoding.test.js" ], "resolutionRationale": "Candidate ownership/race regression tests pass, including retired-port delivery and module failure before binding video. Actual Chromium/Firefox source/main/legacy with both cores verify pause, source change, explicit stop/restart and final close. Direct media contexts deliberately remain player-owned until destroy; disposable stream contexts close on stop. General native fallback/volume/device acceptance remains ASR-05 and SDK-11, not claimed by this specific fix." + }, + { + "id": "ASR-VOLUME-01", + "title": "ASR applies media volume twice and squares playback amplitude", + "confirmation": "reproduced", + "status": "resolved", + "owners": [ + "PKG-ASR-07" + ], + "evidence": [ + "test/browser/asr-playback.spec.js", + "refactor/baselines/asr-volume-validation.json" + ], + "compatibleResolution": "Retain public volume API and apply it once through the media element.", + "closureCriteria": "Native graph amplitude is linear across volume changes, mute and ASR stop/restart; old behavior is separately reproduced.", + "resolutionEvidence": [ + "refactor/changes/2026-09-13-PKG-ASR-07-playback-volume.md", + "refactor/baselines/asr-volume-validation.json" + ], + "resolutionRationale": "Native Chromium/Firefox source/main/legacy on published and candidate core pass relative-amplitude assertions. Formal 2.1 baseline retains squared amplitude. The fix does not establish physical device or captured-fallback acceptance." } ] } diff --git a/refactor/tasks.json b/refactor/tasks.json index 4440d29ba..45de646c5 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -2701,7 +2701,8 @@ ], "dependsOn": [ "PKG-ASR-04", - "CORE-22" + "CORE-22", + "PKG-ASR-07" ], "status": "todo", "risk": "H", @@ -4536,6 +4537,25 @@ "changes/2026-09-13-ENG-12-library-public.md", "baselines/library-public-validation.json" ] + }, + { + "id": "PKG-ASR-07", + "phase": "5 包迁移:artplayer-plugin-asr", + "title": "修复原生播放音量二次衰减", + "scope": [ + "artplayer-plugin-asr" + ], + "dependsOn": [ + "PKG-ASR-04" + ], + "status": "done", + "risk": "H", + "deliverable": "移除重复音量应用;保留新旧原生音频振幅对照及正式产物验收", + "acceptance": "Chromium/Firefox 两核心和 source/main/legacy 输出音量线性、静音和 stop/restart 正确;旧版平方衰减单独保留;单元/类型/真实安装通过,不代替设备验收", + "evidence": [ + "changes/2026-09-13-PKG-ASR-07-playback-volume.md", + "baselines/asr-volume-validation.json" + ] } ] } diff --git a/test/asr-lifecycle.test.js b/test/asr-lifecycle.test.js index ab77568b8..1e5d5adb4 100644 --- a/test/asr-lifecycle.test.js +++ b/test/asr-lifecycle.test.js @@ -404,16 +404,16 @@ test('ASR candidate public stop retains the current hide deadline and allows app assert.equal(source.connections.length, 0) }) -test('ASR candidate volume route survives stop and resume without resetting gain', async () => { +test('ASR candidate playback does not apply media volume a second time before or after stop', async () => { const environment = create({}, { capture: false }) await environment.emit('play') environment.art.volume = 0.25 await environment.emit('video:volumechange') await environment.plugin.stop() const source = environment.nodes.find(node => node.kind === 'element') - assert.equal(source.connections[0].gain.value, 0.25) + assert.equal(source.connections[0].gain.value, 1) await environment.emit('play') - assert.equal(source.connections.find(node => node.kind === 'gain').gain.value, 0.25) + assert.equal(source.connections.find(node => node.kind === 'gain').gain.value, 1) await environment.emit('destroy') assert.equal(environment.contexts[0].closes, 1) }) diff --git a/test/browser/asr-playback.spec.js b/test/browser/asr-playback.spec.js new file mode 100644 index 000000000..e94fa4280 --- /dev/null +++ b/test/browser/asr-playback.spec.js @@ -0,0 +1,114 @@ +import fs from 'node:fs' +import process from 'node:process' +import { verifyAsrContract } from '../../refactor/scripts/asr-contract.mjs' +import { hash, readMember } from '../../refactor/scripts/releases.mjs' +import { compilePackage } from '../helpers/load.js' +import { expect, test } from './fixtures.js' + +let contract +let candidateCode +test.beforeAll(async () => { + contract = await verifyAsrContract() + candidateCode = process.env.ARTPLAYER_ASR_ARTIFACT ? fs.readFileSync(process.env.ARTPLAYER_ASR_ARTIFACT) : await compilePackage('artplayer-plugin-asr', 'umd') +}) + +for (const version of ['2.1.0', 'candidate']) { + for (const core of ['published', 'candidate']) { + test(`ASR ${version} + ${core}: native playback volume and mute`, async ({ page }, testInfo) => { + const code = version === 'candidate' ? candidateCode : readMember(contract.archives.get(version), 'package/dist/artplayer-plugin-asr.js') + await page.goto(`/test/player.html?core=${core}`) + const supported = await page.evaluate(() => typeof AudioContext === 'function' && typeof AudioWorkletNode === 'function') + if (!supported) { + expect(process.platform).toBe('win32') + expect(testInfo.project.name).toBe('webkit') + test.skip(true, 'Windows WebKit lacks native AudioContext/AudioWorkletNode') + } + await testInfo.attach('asr-playback-inputs', { contentType: 'application/json', body: JSON.stringify({ version, core, pluginSha256: hash(code), mediaSha256: hash(fs.readFileSync(new URL('./media/audio-tone.m4a', import.meta.url))), meter: 'Native unity analyser inserted between plugin gain and destination', physicalAudioOutput: false }) }) + await page.addScriptTag({ content: code.toString() }) + await page.evaluate(() => { + const NativeContext = window.AudioContext + window.asrMeters = [] + window.asrChunks = [] + window.AudioContext = class extends NativeContext { + createGain() { + const gain = super.createGain() + const analyser = super.createAnalyser() + analyser.fftSize = 2048 + const connect = gain.connect.bind(gain) + const disconnect = gain.disconnect.bind(gain) + gain.connect = (destination, ...args) => { + if (destination === this.destination) { + connect(analyser) + analyser.connect(destination) + return destination + } + return connect(destination, ...args) + } + gain.disconnect = (...args) => { + analyser.disconnect() + return disconnect(...args) + } + window.asrMeters.push({ gain, analyser }) + return gain + } + } + window.art = new window.Artplayer({ + container: '.player', + url: '/test/audio-tone.m4a', + volume: 1, + loop: true, + plugins: [window.artplayerPluginAsr({ onAudioChunk({ pcm }) { + const samples = new Int16Array(pcm) + window.asrChunks.push(Math.sqrt(samples.reduce((sum, value) => sum + value * value, 0) / samples.length) / 32768) + } })], + }) + document.querySelector('#play').onclick = () => window.art.play() + window.measureOutput = () => { + const meter = window.asrMeters.at(-1) + const samples = new Float32Array(meter.analyser.fftSize) + meter.analyser.getFloatTimeDomainData(samples) + return { volume: window.art.volume, muted: window.art.muted, gain: meter.gain.gain.value, rms: Math.sqrt(samples.reduce((sum, value) => sum + value * value, 0) / samples.length), pcm: window.asrChunks.slice(-3) } + } + }) + await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true) + await page.locator('#play').click() + await expect.poll(() => page.evaluate(() => window.asrChunks.length)).toBeGreaterThanOrEqual(3) + const levels = [] + for (const [volume, muted] of [[1, false], [0.5, false], [0.25, false], [0.5, true], [0.5, false]]) { + await page.evaluate(({ volume, muted }) => { + window.art.volume = volume + window.art.muted = muted + }, { volume, muted }) + await page.waitForTimeout(450) + levels.push(await page.evaluate(() => window.measureOutput())) + } + await testInfo.attach('asr-playback-levels', { contentType: 'application/json', body: JSON.stringify(levels) }) + expect(levels[0].rms).toBeGreaterThan(0.01) + const expected = version === 'candidate' ? [1, 0.5, 0.25, 0, 0.5] : [1, 0.25, 0.0625, 0, 0.25] + for (const [index, level] of levels.entries()) { + expect(level.rms / levels[0].rms).toBeGreaterThanOrEqual(Math.max(0, expected[index] * 0.9)) + expect(level.rms / levels[0].rms).toBeLessThanOrEqual(expected[index] * 1.1) + } + if (version === 'candidate') { + await page.evaluate(() => window.art.plugins.artplayerPluginAsr.stop()) + const count = await page.evaluate(() => window.asrChunks.length) + await page.evaluate(() => { + window.art.volume = 0.25 + }) + await page.waitForTimeout(450) + expect(await page.evaluate(() => window.asrChunks.length)).toBe(count) + const stopped = await page.evaluate(() => window.measureOutput()) + expect(stopped.rms / levels[0].rms).toBeGreaterThan(0.225) + expect(stopped.rms / levels[0].rms).toBeLessThan(0.275) + await page.evaluate(() => window.art.pause()) + await page.locator('#play').click() + await expect.poll(() => page.evaluate(() => window.asrChunks.length)).toBeGreaterThan(count + 2) + const resumed = await page.evaluate(() => window.measureOutput()) + expect(resumed.rms / levels[0].rms).toBeGreaterThan(0.225) + expect(resumed.rms / levels[0].rms).toBeLessThan(0.275) + await testInfo.attach('asr-playback-restart', { contentType: 'application/json', body: JSON.stringify({ stopped, resumed }) }) + } + await page.evaluate(() => window.art.destroy()) + }) + } +}