和安全管理服務(wù)專家新鈦云服 方章和翻譯
Media Capture and Streams API(又名 MediaStream API)允許您從用戶的麥克風(fēng)錄制音頻,然后將錄制的音頻或媒體元素作為音軌獲取。然后,您可以在錄制后直接播放這些曲目,也可以將錄制的媒體上傳到您的服務(wù)器。
在本教程中,我們將創(chuàng)建一個網(wǎng)站,該網(wǎng)站將使用 Media Streams API 來允許用戶錄制某些內(nèi)容,然后將錄制的音頻上傳到服務(wù)器進(jìn)行保存。用戶還可以查看和播放所有上傳的錄音。
您可以在此 https://github.com/sitepoint-editors/mediastream-tutorial 倉庫中找到本教程的完整代碼。
我們將首先基于Node.js創(chuàng)建一個Express服務(wù),如果您的機(jī)器上沒有Node.js,請務(wù)必下載并安裝它。
建一個項(xiàng)目目錄,然后切換到該目錄下:
mkdir recording-tutorialcd recording-tutorial
然后,用npm初始化項(xiàng)目:
npm init -y //選項(xiàng) -y 使用默認(rèn)值創(chuàng)建 package.json
接下來,我們將為我們正在創(chuàng)建的服務(wù)器安裝 Express 并借用nodemon讓其支持熱重啟:
npm i express nodemon
創(chuàng)建一個express服務(wù)
我們現(xiàn)在可以從創(chuàng)建一個簡單的服務(wù)器開始。在根目錄中創(chuàng)建index.js并填寫以下代碼:
const path= require('path');
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.use(express.static('public/assets'));
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`);
});
創(chuàng)建一個服務(wù)器,當(dāng)前環(huán)境中如果沒有啟用了3000端口的話它將在3000端口上運(yùn)行,并且提供了一個靜態(tài)資源目錄public/assets--我們將很快創(chuàng)建它來保存JavaScript和CSS文件以及圖片。
最后,在package.json中的scripts下添加一個啟動腳本:
"scripts": {
"start": "nodemon index.js"
},
讓我們來測試下創(chuàng)建的服務(wù)器,運(yùn)行以下命令啟動服務(wù)器:
npm start
服務(wù)器應(yīng)從3000端口監(jiān)聽,你可以嘗試訪問localhost:3000,但你會看到一個消息說:"Cannot GET /",這是因?yàn)槲覀儧]有定義任何路由。
接下來,我們將創(chuàng)建一個主頁面,用戶將使用此頁面記錄、查看和播放錄音
在public目錄中創(chuàng)建包含以下內(nèi)容的index.html文件:
<!DOCTYPE html>
<html lang="en"><head> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Record</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css"rel="stylesheet"
integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<link href="/css/index.css" rel="stylesheet" />
</head><body class="pt-5">
<div class="container">
<h1 class="text-center">Record Your Voice</h1>
<div class="record-button-container text-center mt-5">
<buttonclass="bg-transparent border btn record-button rounded-circle shadow-sm text-center" id="recordButton">
<img src="/images/microphone.png" alt="Record" class="img-fluid" />
</button>
</div>
</div></body>
</html>
這個頁面使用Bootstrap 5進(jìn)行樣式設(shè)置。目前,該頁面只顯示了一個用戶可以用來錄制的按鈕。
請注意,我們正在使用圖標(biāo)為麥克風(fēng)設(shè)計一個按鈕, 你可以在Iconscout上下載圖標(biāo),也可以使用GitHub存儲庫中的修改。
下載圖標(biāo)并將其放在public/assets/images中,名稱為microphone.png。
我們要引用樣式表index.css,所以創(chuàng)建一個public/assets/css/index.css文件,內(nèi)容如下。
.record-button {
height: 8em;
width: 8em;
border-color: #f3f3f3!important;
}
.record-button:hover {
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
}
最后,我們只需要在index.js中添加新的路由。在app.listen之前添加以下內(nèi)容:
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
如果服務(wù)器還沒有運(yùn)行,使用npm start啟動服務(wù)器,然后在瀏覽器中訪問localhost:3000,您將看到一個記錄按鈕。
目前,這個按鈕沒有任何作用。我們需要綁定一個觸發(fā)錄音的單擊事件。
創(chuàng)建文件public/assets/js/record.js并填入以下內(nèi)容:
//initialize elements we'll useconst recordButton = document.getElementById('recordButton');
constrecordButtonImage = recordButton.firstElementChild;
letchunks = []; //will be used later to record audioletmediaRecorder = null; //will be used later to record audiolet audioBlob = null; //the blob that will hold the recorded audio
然后初始化一個record的函數(shù), 綁定該函數(shù)到點(diǎn)擊事件:
function record() {
//TODO start recording
}
recordButton.addEventListener('click', record);
我們還將此功能作為事件監(jiān)聽器附加到記錄按鈕。
為了開始錄制,我們需要使用mediaDevices.getUserMedia()方法。
這個方法允許我們獲得一個流,只有在用戶同意的情況下,才能錄制用戶的音頻和/或視頻。getUserMedia方法允許我們訪問本地輸入設(shè)備。
getUserMedia接受一個MediaStreamConstraints對象作為參數(shù),它包括一組約束條件,指定我們從getUserMedia獲得的流中的預(yù)期媒體類型。這些約束可以是帶有布爾值的音頻和視頻。
如果值為false,意味著用戶拒絕了這個訪問設(shè)備的行為。
getUserMedia返回一個承諾。如果用戶允許網(wǎng)站進(jìn)行記錄,程序會收到一個MediaStream對象,我們可以用它來對用戶的視頻或音頻流進(jìn)行媒體截取。
為了使用MediaStream API對象來捕獲媒體軌道,我們需要使用MediaRecorder接口。我們需要創(chuàng)建一個該接口的新對象,它在構(gòu)造函數(shù)中接受MediaStream對象,并允許我們通過它的方法輕松地控制錄音。
在記錄函數(shù)中,添加以下內(nèi)容:
//check if browser supports getUserMedia
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
alert('Your browser does not support recording!');
return;
}
// browser supports getUserMedia
// change image in button
recordButtonImage.src = `/images/${mediaRecorder && mediaRecorder.state === 'recording' ? 'microphone' : 'stop'}.png`;
if (!mediaRecorder) {
// start recording
navigator.mediaDevices.getUserMedia({
audio: true,
})
.then((stream) => {
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
mediaRecorder.ondataavailable = mediaRecorderDataAvailable;
mediaRecorder.onstop = mediaRecorderStop;
})
.catch((err) => {
alert(`The following error occurred: ${err}`);
// change image in button
recordButtonImage.src = '/images/microphone.png';
});
} else {
// stop recording
mediaRecorder.stop();
}
我們首先要檢查navigator.mediaDevices和navigator.mediaDevices.getUserMedia是否被定義,因?yàn)橛行g覽器如Internet Explorer、Android上的Chrome或其他瀏覽器并不支持它。
此外,使用getUserMedia需要安全的網(wǎng)站,這意味著要么使用HTTPS、file://或從localhost加載的頁面。所以,如果頁面沒有安全加載,mediaDevices和getUserMedia將無法定義。
如果條件是false(即mediaDevices和getUserMedia都支持),我們首先將錄音按鈕的圖片改為stop.png,你可以從Iconscout或GitHub倉庫下載,并將其放在public/assets/images中。
然后,我們要檢查mediaRecorder--我們在文件開頭定義的--是否為空。
如果它是空的,就意味著沒有正在進(jìn)行的錄制。所以,我們使用getUserMedia獲得一個MediaStream實(shí)例來開始錄制。
我們傳遞給它一個只有鍵值為audio和值為true的對象,因?yàn)槲覀冎皇窃阡浿埔纛l。
這就是瀏覽器提示用戶允許網(wǎng)站訪問麥克風(fēng)的地方。如果用戶允許,程序中的代碼將被執(zhí)行。
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
mediaRecorder.ondataavailable = mediaRecorderDataAvailable;
mediaRecorder.onstop = mediaRecorderStop;
這里我們要創(chuàng)建一個新的MediaRecorder,把它分配給我們在文件開頭定義的mediaRecorder。
我們把從getUserMedia收到的數(shù)據(jù)流傳遞給構(gòu)造函數(shù)。然后,我們使用mediaRecorder.start()開始錄制。
最后,我們將事件處理程序(我們將很快創(chuàng)建)與兩個事件,dataavailable和stop綁定。
我們還添加了一個catch處理程序,以防用戶不允許網(wǎng)站訪問麥克風(fēng)或可能拋出的任何其他異常。
如果mediaRecorder不是空的,這一切都會發(fā)生。如果它是空的,這意味著有一個正在進(jìn)行的錄音,而用戶正在結(jié)束它。所以,我們使用mediaRecorder.stop()方法來停止錄音。
} else {
//stop recording
mediaRecorder.stop();
}
到目前為止,我們的代碼在用戶單擊記錄按鈕時開始和停止記錄。接下來,我們將為可用數(shù)據(jù)添加事件處理程序并停止。
dataavailable事件要么在完成完整的記錄時被觸發(fā),要么基于傳遞給mediaRecorder.start()的可選參數(shù)timeslice來指示該事件應(yīng)該被觸發(fā)的毫秒數(shù)。傳遞timeslice可以對錄音進(jìn)行切片,并以塊狀形式獲取。
創(chuàng)建mediaRecorderDataAvailable函數(shù),該函數(shù)將處理dataavailable事件,只需將收到的BlobEvent參數(shù)中的Blob音軌添加到我們在文件開頭定義的chunks數(shù)組中。
function mediaRecorderDataAvailable(e) {
chunks.push(e.data);
}
chunks是用來保存用戶錄音的音軌數(shù)組。
在我們創(chuàng)建mediaRecorderStop(它將處理停止事件)之前,讓我們首先添加HTML元素容器,它將容納錄制的音頻,并有保存和丟棄按鈕。
在public/index.html中的</body>標(biāo)簽結(jié)束前添加以下內(nèi)容。
<div class="recorded-audio-container mt-5 d-none flex-column justify-content-center align-items-center"
id="recordedAudioContainer">
<div class="actions mt-3">
<button class="btn btn-success rounded-pill" id="saveButton">Save</button>
<button class="btn btn-danger rounded-pill"id="discardButton">Discard</button>
</div>
</div>
然后,在public/assets/js/record.js的開頭,添加一個變量,它將是#recordedAudioContainer元素的一個Node實(shí)例。
const recordedAudioContainer = document.getElementById('recordedAudioContainer');
我們現(xiàn)在可以實(shí)現(xiàn)mediaRecorderStop。這個函數(shù)首先會刪除之前錄制的、沒有保存的任何音頻元素,創(chuàng)建一個新的音頻媒體元素,將src設(shè)置為錄制流的Blob,并顯示容器。
function mediaRecorderStop () {
//check if there are any previous recordings and remove them
if (recordedAudioContainer.firstElementChild.tagName === 'AUDIO') {
recordedAudioContainer.firstElementChild.remove();
}
//create a new audio element that will hold the recorded audio
const audioElm = document.createElement('audio');
audioElm.setAttribute('controls', ''); //add controls
//create the Blob from the chunks
audioBlob = new Blob(chunks, { type: 'audio/mp3' });
const audioURL = window.URL.createObjectURL(audioBlob);
audioElm.src = audioURL;
//show audio
recordedAudioContainer.insertBefore(audioElm, recordedAudioContainer.firstElementChild);
recordedAudioContainer.classList.add('d-flex');
recordedAudioContainer.classList.remove('d-none');
//reset to default
mediaRecorder = null;
chunks = [];
}
最后,我們要把mediaRecorder和chunks重置為初始值,以處理接下來的錄音。有了這段代碼,我們的網(wǎng)站應(yīng)該能夠錄制音頻,當(dāng)用戶停止時,它允許他們播放錄制的音頻。
我們需要做的最后一件事是在index.html中鏈接到record.js。在body末尾添加該腳本。
<script src="/js/record.js"></script>
現(xiàn)在讓我們來看看,在你的瀏覽器中訪問localhost:3000,點(diǎn)擊錄音按鈕會詢問是否允許網(wǎng)站使用麥克風(fēng)
請確保你在本地主機(jī)或HTTPS服務(wù)器上加載網(wǎng)站,即使你使用的是支持的瀏覽器。在其他條件下,MediaDevices和getUserMedia是不可用的。
點(diǎn)擊 "允許"。然后,麥克風(fēng)的圖像將變?yōu)橥V沟膱D像。同時,根據(jù)你的瀏覽器,你應(yīng)該在地址欄看到一個錄音圖標(biāo)。這表明麥克風(fēng)目前已被網(wǎng)站訪問。
試著錄制幾秒鐘。然后點(diǎn)擊停止按鈕。按鈕的圖像將變回麥克風(fēng)的圖像,音頻播放器將顯示兩個按鈕--保存和取消。
接下來,我們將實(shí)現(xiàn)保存和取消按鈕的點(diǎn)擊事件。保存按鈕應(yīng)該將音頻上傳到服務(wù)器,而取消按鈕應(yīng)該將其刪除。
我們首先要實(shí)現(xiàn)取消按鈕的事件處理程序。點(diǎn)擊這個按鈕應(yīng)該首先向用戶顯示一個提示,讓他們確認(rèn)是否要放棄錄音。然后,如果用戶確認(rèn)了,它將移除音頻播放器并隱藏按鈕。
在public/assets/js/record.js的開頭添加將容納取消按鈕的變量。
const discardAudioButton = document.getElementById('discardButton');
function discardRecording () {
//show the user the prompt to confirm they want to discard
if (confirm('Are you sure you want to discard the recording?')) {
//discard audio just recorded
resetRecording();
}
}
functionresetRecording () {
if (recordedAudioContainer.firstElementChild.tagName === 'AUDIO') {
//remove the audio
recordedAudioContainer.firstElementChild.remove();
//hide recordedAudioContainer
recordedAudioContainer.classList.add('d-none');
recordedAudioContainer.classList.remove('d-flex');
}
//reset audioBlob for the next recording
audioBlob = null;
}
//add the event listener to the button
discardAudioButton.addEventListener('click', discardRecording);
您現(xiàn)在測試一下,然后單擊取消按鈕,音頻播放器將被刪除,按鈕會隱藏。
現(xiàn)在,我們將實(shí)現(xiàn) "保存 "按鈕的點(diǎn)擊處理程序。當(dāng)用戶點(diǎn)擊保存按鈕時,該處理程序?qū)⑹褂肍etch API將audioBlob上傳至服務(wù)器。
如果你對Fetch API不熟悉,可以在我們的 "Fetch API介紹 "教程中了解更多。
讓我們先在項(xiàng)目根目錄下創(chuàng)建一個uploads目錄:
mkdir uploads
然后,在record.js的開頭,添加一個變量來保存Save按鈕元素:
constsaveAudioButton = document.getElementById('saveButton');
functionsaveRecording () {
//the form data that will hold the Blob to upload constformData = new FormData();
//add the Blob to formData
formData.append('audio', audioBlob, 'recording.mp3');
//send the request to the endpoint
fetch('/record', {
method: 'POST',
body: formData
})
.then((response) => response.json())
.then(() => {
alert("Your recording is saved");
//reset for next recording
resetRecording();
//TODO fetch recordings
})
.catch((err) => {
console.error(err);
alert("An error occurred, please try again later");
//reset for next recording
resetRecording();
})
}
//add the event handler to the click event
saveAudioButton.addEventListener('click', saveRecording);
注意,一旦錄音被上傳,我們就用resetRecording來重置下一個錄音的音頻。稍后,我們將獲取所有的錄音,向用戶展示這些錄音。
我們現(xiàn)在需要實(shí)現(xiàn)上傳API。這個API將把音頻上傳到uploads目錄中。
為了在Express中輕松處理文件上傳,我們將使用Multer庫。Multer提供了一個處理文件上傳的中間件。
運(yùn)行以下程序來安裝它:
npm i multer
// 在Index.js中添加以下內(nèi)容const fs = require('fs');
constmulter = require('multer');
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, 'uploads/');
},
filename(req, file, cb) {
const fileNameArr = file.originalname.split('.');
cb(null, `${Date.now()}.${fileNameArr[fileNameArr.length - 1]}`);
},
});
const upload = multer({ storage });
我們使用multer.diskStorage聲明了存儲,我們將其配置為在uploads目錄下存儲文件,并且我們將根據(jù)當(dāng)前的時間戳與擴(kuò)展名來保存文件。
然后,我們聲明了upload,這將是上傳文件的中間件。
接下來,我們要使uploads目錄中的文件可以公開訪問。所以,在app.listen前添加以下內(nèi)容。
app.use(express.static('uploads'));
最后,我們將創(chuàng)建上傳接口。這個接口將只是使用上傳中間件來上傳音頻并返回一個JSON響應(yīng)。
app.post('/record', upload.single('audio'), (req, res) => res.json({ success: true }));
上傳中間件將處理文件的上傳。我們只需要把我們要上傳的文件的字段名傳遞給upload.single。
請注意,通常情況下,你需要對文件進(jìn)行驗(yàn)證,確保上傳的是正確的、預(yù)期的文件類型。為了簡單起見,我們在本教程中省略了這一點(diǎn)。
讓我們來測試一下。再次進(jìn)入訪問localhost:3000,錄制一些東西,然后點(diǎn)擊保存按鈕。
請求上傳接口,文件將被上傳,并將向用戶顯示一個提示,通知他們錄音已被保存。
你可以通過檢查你項(xiàng)目根部的uploads目錄來確認(rèn)音頻是否真的被上傳。你應(yīng)該在那里找到一個MP3音頻文件。
創(chuàng)建相關(guān)接口:
我們要做的最后一件事是向用戶展示所有的錄音,以便他們可以播放。
首先,我們要創(chuàng)建一個接口,用來獲取所有的文件。在index.js的app.listen前添加以下內(nèi)容。
app.get('/recordings', (req, res) => {
letfiles = fs.readdirSync(path.join(__dirname, 'uploads'));
files = files.filter((file) => {
// check that the files are audio files
const fileNameArr = file.split('.');
return fileNameArr[fileNameArr.length - 1] === 'mp3';
}).map((file) => `/${file}`);
return res.json({ success: true, files });
});
我們只是在讀取uploads目錄下的文件,過濾它們,只得到mp3文件,并在每個文件名后加上一個/。最后,我們將返回一個包含文件的JSON對象。
在html中展示:
<h2 class="mt-3">Saved Recordings</h2>
<div class="recordings row"id="recordings">
</div>
從API中獲取上傳的文件:
const recordingsContainer = document.getElementById('recordings');
function fetchRecordings () {
fetch('/recordings')
.then((response) => response.json())
.then((response) => {
if (response.success && response.files) {
//remove all previous recordings shown
recordingsContainer.innerHTML = '';
response.files.forEach((file) => {
//create the recording element
const recordingElement = createRecordingElement(file);
//add it the the recordings container
recordingsContainer.appendChild(recordingElement);
})
}
})
.catch((err) => console.error(err));
}
//create the recording element
function createRecordingElement (file) {
//container element
const recordingElement = document.createElement('div');
recordingElement.classList.add('col-lg-2', 'col', 'recording', 'mt-3');
//audio element
const audio = document.createElement('audio');
audio.src = file;
audio.onended = (e) => {
//when the audio ends, change the image inside the button to play again
e.target.nextElementSibling.firstElementChild.src = 'images/play.png';
};
recordingElement.appendChild(audio);
//button element
const playButton = document.createElement('button');
playButton.classList.add('play-button', 'btn', 'border', 'shadow-sm', 'text-center', 'd-block', 'mx-auto');
//image element inside button
const playImage = document.createElement('img');
playImage.src = '/images/play.png';
playImage.classList.add('img-fluid');
playButton.appendChild(playImage);
//add event listener to the button to play the recording
playButton.addEventListener('click', playRecording);
recordingElement.appendChild(playButton);
//return the container element
return recordingElement;
}
function playRecording (e) {
let button = e.target;
if (button.tagName === 'IMG') {
//get parent button
button = button.parentElement;
}
//get audio sibling
const audio = button.previousElementSibling;
if (audio && audio.tagName === 'AUDIO') {
if (audio.paused) {
//if audio is paused, play it
audio.play();
//change the image inside the button to pause
button.firstElementChild.src = 'images/pause.png';
} else {
//if audio is playing, pause it
audio.pause();
//change the image inside the button to play
button.firstElementChild.src = 'images/play.png';
}
}
}
注意,在playRecording函數(shù)中,我們使用audio.paused來檢查音頻是否正在播放,如果音頻目前沒有播放,它將返回true。
我們還使用了播放和暫停的圖標(biāo),這些圖標(biāo)將顯示在每個錄音中。你可以從Iconscout或GitHub資源庫中獲得這些圖標(biāo)。
當(dāng)頁面加載和新的錄音被上傳時,我們將使用fetchRecordings。
所以,在record.js的結(jié)尾和saveRecording的履行處理程序里面調(diào)用這個函數(shù),以代替TODO注釋。
.then(() => {
alert("Your recording is saved");
//reset for next recording
resetRecording();
//fetch recordings
fetchRecordings();
})
增加相關(guān)樣式:
public/assets/css/index.css
.play-button:hover {
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
}
.play-button {
height: 8em;
width: 8em;
background-color: #5084d2;
}
最終測試
現(xiàn)在都準(zhǔn)備好了。在你的瀏覽器中打開localhost:3000的網(wǎng)站,如果你之前上傳了任何錄音,你現(xiàn)在會看到它們。你也可以嘗試上傳新的,看到列表被更新。用戶現(xiàn)在可以錄制他們的聲音,保存或丟棄它們。用戶還可以查看所有上傳的錄音并播放它們。
常聽到剛開始做微課的老師說,錄制的微課沒有聲音,對于新手,怎么解決這個問題呢?
錄制微課的過程中,涉及到的聲音無非就是兩種:一種是語音錄入,一種是錄制電腦的系統(tǒng)聲音。
準(zhǔn)備工作:
1、臺式機(jī)的外接麥克風(fēng)是否連接正確,通常,麥克風(fēng)的插頭處是有顏色的(通常是粉紅色),插入臺式機(jī)電腦的聲卡對應(yīng)顏色的插孔就OK。
2、筆記本電腦通常是內(nèi)置麥克風(fēng)(有個別款式的筆記本電腦是不帶內(nèi)置麥克風(fēng)的)
確認(rèn)硬件設(shè)備無誤之后,還是無法錄制語音,通常和你所用的微課制作軟件沒什么關(guān)系,而是在電腦系統(tǒng)設(shè)置里的聲音設(shè)置可能不正確導(dǎo)致,下面是在win7下和xp下麥克風(fēng)錄音的設(shè)置方法,寫的比較詳細(xì),還配有截圖,大家可以對照著看一下。
win7下麥克風(fēng)錄音設(shè)置方法步驟:
http://jingyan.baidu.com/article/60ccbceb1f1d7f64cab19719.html
Windows XP 下聲卡的錄音和麥克風(fēng)設(shè)置方法:
http://wenku.baidu.com/view/1ce5f76327d3240c8447ef93.html
測試
打開操作系統(tǒng)里的 程序——附件——錄音機(jī),點(diǎn)擊開始錄音,然后你開始發(fā)聲,這時如果你看到綠色的音量指示器在跳動,那就說明麥克風(fēng)是設(shè)置好了的,回放聲音也就可以聽到剛才你錄入的語音了。
點(diǎn)擊“開始”菜單里的“錄課”按鈕
在彈出的菜單里,按照文字提示選擇要錄制的內(nèi)容就可以了
語音:是指錄制麥克風(fēng)語音
拍攝:可以在錄課時調(diào)用攝像頭拍攝
鼠標(biāo)軌跡:錄課時錄制鼠標(biāo)軌跡(可以選擇多種軌跡光標(biāo))
自動降噪:錄制語音時勾選此項(xiàng)
錄制系統(tǒng)聲音:勾選上可以錄制電腦播放的聲音。
要進(jìn)入設(shè)置菜單里,確認(rèn)錄音設(shè)備是否選中
面,我們通過Web Audio API(網(wǎng)頁音頻接口)制作了一個簡單的剛琴小程序。(見《利用Web Audio API,使用純ECMAScript 6,無需第三方庫,開發(fā)一個鋼琴小程序》)
我們現(xiàn)在看看Web Audio API(網(wǎng)頁音頻接口)是如何來處理錄音的。有些聲音是很難使用振蕩器來重現(xiàn)的,所以在許多情況下必須使用錄制的聲音來重現(xiàn)真實(shí)的聲音。格式可以是".MP3",".ogg",".wav"等,本人建議使用".MP3",因?yàn)樗禽p量級,有廣泛支持且具有很好的音質(zhì)。
你不能像圖片一樣簡單地通過一個網(wǎng)址得到聲音。我們需要通過發(fā)送一個XMLHttpRequest請求來得到文件,解碼數(shù)據(jù),并放入緩沖區(qū)。
讓我們看看構(gòu)造函數(shù): 我們收到我們在聲音類中所做的Context;收到的Url列表將被加載;一個空數(shù)組用于緩沖。
我們有兩個方法:loadsound和loadAll。 loadAll循環(huán)通過URL列表來調(diào)用loadSound方法。它重要的是傳遞索引,我們才能在不管哪個請求首先加載下把緩沖的聲音放入正確的數(shù)組元素。這也讓我們看到最后一個請求意味著緩沖區(qū)加載的完成。
然后你可以調(diào)用loaded()方法來做一些像隱藏加載指示器之類事情。最后在播放時通過getSoundByIndex(index) 方法從播放緩沖區(qū)中獲取相應(yīng)的值。
decodeAudioData方法是新的語法,現(xiàn)在還不能在Safari中工作。
context.decodeAudioData(audioData).then(function(decodedData) {
// use the decoded data here
});
然后我們必須為聲音創(chuàng)建類。現(xiàn)在我們有完整的類來處理錄音:
構(gòu)造函數(shù)接收了Context和Buffer緩沖。我們調(diào)用createBufferSource()方法來代替我們以前做的createOscillator()方法。我們使用getSoundByIndex()方法來獲取緩沖的音符(從緩沖數(shù)組中來的元素)。現(xiàn)在我們創(chuàng)建緩沖區(qū)(而不是振蕩器),設(shè)置緩沖區(qū),然后將其連接到目標(biāo)(或增益和其他濾波器)。
現(xiàn)在我們要創(chuàng)建緩沖區(qū)實(shí)例并調(diào)用loadAll方法來加載所有的聲音到緩沖區(qū)。我們同時也要用getSoundById方法來獲取我們真正所需要的,把它傳送給聲音類并調(diào)用play()方法。ID可以作為按鈕上的數(shù)據(jù)屬性存儲,你點(diǎn)擊則播放聲音。
這里有一個在線實(shí)例,使用了上述緩沖區(qū)、錄制的音符等技術(shù)(由于音頻資源較多,未完整實(shí)現(xiàn),僅供參考):
http://www.ikinsoft.com/3ddemo/recoder.html
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。