探索js讓你的網頁「本身開口說話」




本文爲做者行舟客投稿,原文地址爲https://yunxiaomeng.blog.csdn.net/article/details/108672415前端

 歡迎點贊!web




背景

最近一直在研究音視頻流,正好想要作一個「有聲提示」,加強頁面交互和用戶體驗的功能。 (之後打算引入前端AI,讓整個頁面真正實現「語音控制」,嘿嘿)。 這一想,順便就想到了讓一衆網友爲難的「網頁自動播放音視頻」。

不說廢話,實現過程當中卻是遇到了一點小問題:canvas

正文

原本嘛覺得是很簡單的:就像通常給網頁添加背景音樂,先動態建立一個audio元素,讓其隱藏起來,而後用js添加一個event事件,並觸發(事實上如今廣泛認爲的是:不能給網頁添加背景音樂。但通過猜測和實踐發現,也可經過下文第一種解決方式實現):後端

let event = new MouseEvent("click"); // 建立一個單擊事件
//給觸發元素添加屬性或者回調事件
//好比:a.download = xxx  /  div.onclick=function(){}
觸發元素.dispatchEvent(event); // 觸發單擊事件

可是當筆者用Google瀏覽器打卡後發現:瀏覽器禁止audio/video自動播放了:autoplay被禁用了! 除非用戶主動打開設置api

好吧聽着就有點匪夷所思,通過筆者屢次測試得出——跨域

  1. 在controls屬性存在時video容許autoplay控制自動播放,controls不存在時其autoplay屬性不起做用;(Google)
  2. audio元素的autoplay屬性永遠不起做用(必須用戶手動觸發);(Google和Firefox)
  3. video元素是支持source兼容的!可是若是source中type爲音頻相關格式,那麼autoplay大機率也不起做用;(Google和Firefox等)
  4. 文檔中要求的「手動觸發」很嚴格——按道理講上面代碼建立一個鼠標事件其意義和用戶手動按下鼠標按鈕是同樣的,可是,瀏覽器不支持!

因此咱們能夠換句話說:mp3等音頻格式的autoplay被Google禁用了!promise

沒有辦法,我只能將目光移向「元素click事件」,後來想來是我多想了——由於上面第四點的緣故,任何非用戶手動操做的「動做」都會被瀏覽器禁止。瀏覽器

Google瀏覽器爲其取了一個頗有意義的名字 —— 自動播放阻止。微信


可是我仍是發現了文檔中的一個「新」成員:web Audio API。雖然文檔中沒有描述什麼相關原理,可是經過這段描述:image.png這讓我忽然的就想到了HTML5的另外一個「大殺器」:canvas。他也是經過一個上下文對象context凌駕於瀏覽器圖像/視頻流之上。app

web audio api怎麼說呢,感受至少目前對大多數人用處真不大——文檔中&被廣大開發者發掘的各類騷操做canvas能作的都用canvas了,canvas不能作的對絕大多數開發者來講也不重要。

順着這個思路,我想到了「建立一個音軌上下文,在其中用 createBufferSource() 方法用於建立一個新的AudioBufferSourceNode接口—— 該接口能夠經過AudioBuffer對象 來播放音頻數據,以突破瀏覽器限制」,AudioBuffer對象怎麼獲取?web audio API爲了解決音頻源的問題,提出了「向音頻url發送一個請求,將數據以arraybuffer返回,再解碼獲得元數據」這樣看似複雜的方法,具體過程是這樣的:

方法使用XHR加載一個音軌,設置請求的responsetype爲ArrayBuffer使它返回一個arraybuffer數據,而後存儲在audioData變量中。而後咱們將這個arraybuffer數據置於decodeAudioData()方法中使用,當成功解碼PCM Data後經過promise回調返回, 將返回的結果經過AudioContext.createBufferSource()接口進行處理並得到一個AudioBufferSourceNode,,將源鏈接至AudioContext.destination造成一個完整的音頻流。

var context = new (window.AudioContext || window.webkitAudioContext)();
var soundBuffer = null;

function loadSound(url{
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';

    request.onload = function({
        context.decodeAudioData(request.response).then((buffer)=>{
            soundBuffer = buffer;
            playSound(soundBuffer);
        });
    };
    request.send();
}

function playSound(buffer{
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source[source.start?"start":"noteOn"](0);
}
//你也能夠將init放在某個按鈕觸發事件上
window.addEventListener('DOMContentLoaded', init, false);

function init({
    try {
        var url = "audio/1.mp3";
        loadSound(url);
    } catch (e) {
        alert('你的瀏覽器不支持Web Audio API');
    }
}

這裏有兩個注意點:

  1. 倒數第六行url那裏若是是個音頻的話最好仍是手動下載到本地,否則極有可能涉及到跨域問題
  2. 若是是要「語音提示某一段話」,則能夠爲代碼中 init() 函數增長txt參數,並調用百度轉化接口,將文字先用encodeURI API轉化爲uri格式,再接入百度接口 var url = "http://tts.baidu.com/text2audio?lan=zh&ie=UTF-8&text=" + encodeURI('這裏是字符串文本'); 轉化爲url連接

這段代碼很神奇:google不支持,可是控制檯裏面「萬惡的報錯」也沒有了,會給你一個提示:image.png總之就是我不給你播放。

因此,在除了Google的其他瀏覽器中,建議將上面的API和下面這段HTML代碼一塊兒寫上:

<video controls autoplay style="display:none;">   /** 或者將controls和style都去掉 */
 <source src="這裏寫要播放的音頻路徑,如:http://m10.music.126.net/20200926133756/cba79f37e90871df07cd582befe27723/ymusic/obj/w5zDlMODwrDDiGjCn8Ky/2920523350/fd2a/c111/aae2/5542b627692d3df8d63bbaeb1c73711a.mp3" type="audio/mp3"></source>
</video>

而後你應該就能夠聽到美妙動聽的背景音了!

Firefox和Edge雖然禁掉了autoplay,可是它也是支持AudioContext API的!Google中禁止了一切不符合規範的API,並且多是由於某些不知名緣由,上面這段HTML代碼在Google中是時而能夠時而不行的,就很迷惑。。。


補充其實這個API最大的做用是用於音頻可視化領域——它有一個函數是這樣的:createAnalyser() 用來建立一個音域可視化對象,能夠將它鏈接到context流上:

var gainNode=context[context.createGain?"createGain":"createGainNode"]();
gainNode.connect(context.destination);
//操做完上一步後,咱們已經將音域加載到context流上,之後的操做就能夠直接鏈接這個音域對象
var analyser=context.createAnalyser();
analyser.fftSize=512;
analyser.connect(gainNode);


發現瀏覽器中有了部分實現的相關API——它曾經一直在逃避瀏覽器音頻播放政策:

function speak(sentence,pitch,volume{   //使用時調用這個函數便可
    const utterance = new SpeechSynthesisUtterance(sentence);
    //音調
    utterance.pitch = pitch;
    //音量
    utterance.volume = volume;
    window.speechSynthesis.speak(utterance)
}

參數 sentence 是一個字符串格式文本。

只要調用了這個函數並傳入參數,你就能在瀏覽器中聽到動聽的、求之不得的聲音了!(仍是個女聲,嘿嘿嘿) 目前, Chrome70已經廢棄這個API了。。。(由於它能夠不通過用戶主動觸發而直接播放)image.png


固然還有穩妥一些的作法,也是個讓人比較眼前一亮的操做:是MDN文檔中提到的 Feature-Policy 頭——HTTP頭信息,可設置自動播放相關:這個屬性是給後端用的:在response中設置header!



最後

  • 歡迎加我微信(winty230),拉你進技術羣,長期交流學習...

  • 歡迎關注「前端Q」,認真學前端,作個專業的技術人...

image.png

相關文章
相關標籤/搜索