当页面自动刷新但在谷歌浏览器中没有时,语音在 Firefox 中被切断

Posted

技术标签:

【中文标题】当页面自动刷新但在谷歌浏览器中没有时,语音在 Firefox 中被切断【英文标题】:Speech gets cut off in firefox when page is auto-refreshed but not in google chrome 【发布时间】:2019-06-17 02:54:03 【问题描述】:

我有这个问题,在 Firefox 中,如果页面自动刷新,语音会被切断,但在谷歌浏览器中,即使页面自动刷新,它也会完成说语音。如何解决这个问题,即使页面自动刷新,Firefox 中的语音也不会被截断?

msg = new SpeechSynthesisUtterance("please finish saying this entire sentence.");
window.speechSynthesis.speak(msg);

(function ($) 
  'use strict';
  if (window == window.top) 
    var body = $('body').empty();
    var myframe = $('<iframe>')
      .attr( src: location.href )
      .css( height: '95vh', width: '100%' )
      .appendTo(body)
      .on('load', function () 
        var interval;
        interval = 750; 
        setTimeout(function () 
          myframe.attr( src: location.href );
        , interval);
      );
  
)(jQuery);

【问题讨论】:

页面如何自动刷新? (例如通过 html 元标记?)或者您的意思是按 F5/单击刷新? @Frosty.. (1) 是否可以制作一个基本的演示页面来展示整个问题的实际运行情况?也许我们可以修复这样的代码。 (2)发布的代码不足以重新创建您的问题。您的自动刷新技术究竟是什么? (是通过浏览器扩展?innerHTML 代码?javascript 代码?).. 如果我们知道,也许可以想到一个解决方法。 @VC.One 我发布了刷新脚本。 嗨,我在 Firefox 中能做到的最好的方法是:Web Speech demo(及其相关的source code)。如果它正在做你想做的事,那么请告诉我让它也适用于你的网站/内容。 我问 “是否可以制作一个基本的演示页面来显示整个问题的实际运行情况?” 是为了找出(查看)为什么您需要每次刷新750 毫秒。例如,如果因为某些体育赛事比分而更新。在这种情况下,最好更新包含“分数”文本本身的 div(而不是刷新整个页面)。 【参考方案1】:

我有这个问题,在 Firefox 中,如果 页面是自动刷新的,但在谷歌浏览器中它会说完 即使页面是自动刷新的,也可以语音。

所描述的 Firefox 行为是合理的预期实现。

浏览 Firefox 和 Chromium 的 source code,speechSynthesis.speak() 的实现基于与本地语音服务器的套接字连接。 *nix 的服务器通常是speech-dispatcherspeechd (speech-dispatcher)。有关尝试在 Chromium 上实现 SSML 解析的描述,请参阅 How to programmatically send a unix socket command to a system server autospawned by browser or convert JavaScript to C++ souce code for Chromium?。

最终决定编写自己的代码来根据 W3C 规范 SpeechSynthesisSSMLParser 使用 JavaScript 来实现这一要求,在 SE 站点提出多个问题、提交问题和错误并在 W3C 邮件列表上发布之后,没有任何证据表明 SSML 解析会曾经作为 Web Speech API 的一部分包含在内。

一旦启动该连接,就会为调用.speak() 创建一个队列。即使连接关闭Task Manager 仍可能显示服务注册的活动进程。

Chromium/Chrome 的过程并非没有错误,最接近问题描述的是

Issue 797624: "speak speak slash" is audio output of .speak() following two calls to .speak(), .pause() and .resume()

Why hasn't Issue 88072 and Issue 795371 been answered? Are Internals>SpeechSynthesis and Blink>Speech dead?(可能的原因是“但在谷歌浏览器中,即使页面是自动刷新的,它也会完成讲话。”在 Chrome 中仍然可以)

.volume属性问题

Issue 797512: Setting SpeechSynthesisUtterance.volume does not change volume of audio output of speechSynthesis.speak()(铬/铬)

Bug 1426978 Setting SpeechSynthesisUtterance.volume does not change volume of audio output of speechSynthesis.speak() (火狐)

最令人震惊的问题是 Chromium/Chrome webkitSpeechReconition 实现,它记录用户的音频并将该音频数据发布到远程服务,在那里将脚本返回到浏览器 - 没有明确通知正在发生的用户,标记为WONT FIX

Issue 816095: Does webkitSpeechRecognition send recorded audio to a remote web service by default?

GitHub 上的相关 W3C 语音 API 问题

The UA should be able to disallow speak() from autoplaying #27

Precisely define when speak() should fail due to autoplay rules #35(具有讽刺意味的是,与报告的 Chromium/Chrome 行为和此问题中描述的输出相关,请参阅 Web Audio, Autoplay Policy and Games 和 Autoplay Policy Changes)

Intent to Deprecate: speechSynthesis.speak without user activation

总结

SpeechSynthesis API 在网络上被积极滥用。我们没有关于滥用的确切数据,但由于其他自动播放途径是 开始关闭,滥用传闻转移到网络语音 API,不遵循自动播放规则。

弃用后,计划是使 SpeechSynthesis.speak 到 如果没有特定的自动播放规则,则立即触发错误 使满意。这将使其与 Chrome 中的其他音频 API 保持一致。

Timing of SpeechSynthesis state changes not defined #39

Timing of SpeechSynthesisUtterance events firing not defined #40

Clarify what happens if two windows try to speak #47


总之,不会将 Firefox 的行为描述为“问题”,而是将 Chrome 的行为描述为潜在的“问题”。

在浏览器中深入研究 W3C Web Speech API 实现并非易事。有几个原因。包括商业 TTS/SST 服务的明显重点或可用选项,以及“智能手机”中语音合成和语音识别的专有闭源实现;代替解决在现代浏览器中实际部署 W3C Web Speech API 的各种问题。

speechd (speech-dispatcher) 的维护者对服务器端(本地 speech-dispatcher 套接字)非常有帮助。

不能代表 Firefox 维护人员。估计如果提交与.speak() 从重新加载的window 继续执行音频输出的功能请求相关的错误,则不太可能与浏览器实现的最新自动播放策略一致。尽管您仍然可以提交 Firefox 错误以询问在重新加载当前 window 期间是否预计音频输出(来自任何 API 或接口)会继续;以及是否可以设置任何偏好或策略来覆盖所描述的行为,如@zip 的回答所建议的那样。并从实施者自己那里得到答案。

编写 FOSS 代码的个人和团体在域中很活跃并愿意帮助 SST/TTS 开发,其中许多人在 GitHub 上很活跃,这是询问有关如何实现您正在尝试的内容的另一个选项具体在火狐浏览器上实现。

除了向实施者询问功能请求之外,您还可以阅读源代码并尝试创建一个或多个解决方法。替代方法包括使用meSpeak.js,但如果 Firefox 在重新加载window 期间 故意阻止音频输出,这仍然不一定能解决。

【讨论】:

感谢您解释为什么它不起作用,但是您有解决方案让它起作用吗?【参考方案2】:

不确定为什么行为会有所不同...guest271314 可能会在他的回答中有所作为。但是,您可以通过使用onbeforeunload 处理程序拦截 reload 事件并等待话语完成来防止 FF 停止 tts:

msg = new SpeechSynthesisUtterance("say something");
window.speechSynthesis.speak(msg);
window.onbeforeunload = function(e) 
  if(window.speechSynthesis.speaking)
    event.preventDefault();
    msg.addEventListener('end', function(event) 
      //logic to continue unload here
    );
  
;

【讨论】:

【参考方案3】:

已编辑:在初始答案下方查看带有承诺的更优雅的解决方案!


下面的 sn-p 是针对 Firefox 中发现的浏览器不一致的解决方法,检查间隔中的 synth.speaking 并仅在 false 时触发重新加载,以防止合成器过早切割:

(它在 SO sn-p 中不能正常工作,我认为它不喜欢 iFrames 中的 iFrames 或其他任何东西,只需将代码复制粘贴到文件中并用 Firefox 打开它!) em>

<p>I'm in the body, but will be in an iFrame</p>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
	var synth = window.speechSynthesis;
	msg = new SpeechSynthesisUtterance("please finish saying this entire sentence.");
	synth.speak(msg);
	
	(function ($) 
		'use strict';
		if (window == window.top) 
			var body = $('body').empty();
			var myframe = $('<iframe>')
				.attr( src: location.href )
				.css( height: '95vh', width: '100%' )
				.appendTo(body)
				.on('load', function () 
					var interval;
					interval = setInterval(function () 
						if (!synth.speaking) 
							myframe.attr( src: location.href );
							clearInterval(interval);
						
					, 750);
				);
		
	)(jQuery);
</script>

更复杂的解决方案可能是根本没有任何setTimeout()setInterval(),而是使用promise。像这样,无论消息多短或多长,只要消息完成合成,页面就会简单地重新加载。这也将防止在初始页面加载时出现“双重”/重叠语音。不确定这是否对您的方案有帮助,但您可以这样做:

<button id="toggleSpeech">Stop Speaking!</button>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
	if (window == window.top) 
		window.speech = 
			say: function(msg) 
				return new Promise(function(resolve, reject) 
					if (!SpeechSynthesisUtterance) 
						reject('Web Speech API is not supported');
					
					
					var utterance = new SpeechSynthesisUtterance(msg);
					
					utterance.addEventListener('end', function() 
						resolve();
					);
					
					utterance.addEventListener('error', function(event) 
						reject('An error has occurred while speaking: ' + event.error);
					);
					
					window.speechSynthesis.speak(utterance);
				);
			,
			speak: true,
		;
	
	
	
	(function($) 
		'use strict';

		if (window == window.top) 
			var body = $('body').empty();
			var myframe = $('<iframe>')
				.attr( src: location.href )
				.css( height: '95vh', width: '100%' )
				.appendTo(body)
				.on('load', function () 
					var $iframe = $(this).contents();

					$iframe.find('#toggleSpeech').on('click', function(e) 
						console.log('speaking will stop when the last sentence is done...');
						window.speech.speak = !window.speech.speak;
					);
					
					window.speech.say('please finish saying this entire sentence.')
						.then(function() 
							if ( window.speech.speak ) 
								console.log('speaking done, reloading iframe!');
								myframe.attr( src: location.href );
							
						);
				);
		
	)(jQuery);
</script>

注意:Chrome(自 v70 起)不再允许立即调用 window.speechSynthesis.speak(new SpeechSynthesisUtterance(msg)),您将收到错误 speechSynthesis.speak() without user activation is no longer allowed...,更多详细信息 here。所以从技术上讲,用户必须在 Chrome 中激活脚本才能使其工作!

【讨论】:

【参考方案4】:

火狐:

首先在浏览器的地址栏中输入并搜索“about:config”。这将转到另一个页面,其中将弹出一个要求承担任何风险的弹出窗口,您需要接受这一点。从列表中查找名为“accessibility.blockautorefresh”的首选项,然后右键单击它。屏幕上会有一些选项以列表的形式出现,选择 Toggle 选项,然后将其设置为 True 而不是 False。此更改将阻止 Firefox 浏览器上的自动刷新。请记住,此选项是可恢复的!

【讨论】:

这和frosty的问题没有任何关系,这是一个为什么不同的浏览器在给定的场景下表现不同的问题;您正在就不同的问题向浏览器 usage 提供建议。请尝试确保您实际上是在回答原始问题;谢谢 你好,马库斯,我根据要求给出了解决方案以在 Firefox 中解决? @zip 你说 “这将阻止 Firefox 上的自动刷新” bu Asker 不想阻止它。即使进行一些自动刷新,他也希望当前音频继续播放(不剪切或停止)。

以上是关于当页面自动刷新但在谷歌浏览器中没有时,语音在 Firefox 中被切断的主要内容,如果未能解决你的问题,请参考以下文章

CSS样式有时仅在jsf页面上的页面刷新后应用?

当我输入单元格时自动获取日期和时间,但在谷歌表格的单独列中

谷歌浏览器chrome怎样实现间隔自动刷新网页

在谷歌浏览器中使黄色自动完成透明[重复]

如何调试nodejs程序 在谷歌浏览器

CSS禁止Chrome谷歌浏览器激活输入框后自动添加橘黄色边框