
image
問(wèn)題所在:做項(xiàng)目的時(shí)候,要求能在平臺(tái)端播放app上傳而來(lái)的錄音文件,而安卓端目前只能錄制"amr"、"3gp"格式,完美的避開(kāi)了html支持的三種音頻格式。
我們選擇了每分鐘約100kb的“amr”格式來(lái)完成錄制和上傳功能,而在html5中:
video 元素支持三種視頻格式:
| 格式 | IE | Firefox | Opera | Chrome | Safari |
|---|---|---|---|---|---|
| Ogg | No | 3.5+ | 10.5+ | 5.0+ | No |
| MPEG 4 | 9.0+ | No | No | 5.0+ | 3.0+ |
| WebM | No | 4.0+ | 10.6+ | 6.0+ | No |
audio 元素支持三種音頻格式:
| 格式 | IE 9 | Firefox 3.5 | Opera 10.5 | Chrome 3.0 | Safari 3.0 |
|---|---|---|---|---|---|
| Ogg Vorbis | √ | √ | √ | ||
| MP3 | √ | √ | √ | ||
| Wav | √ | √ | √ |
解決辦法:我在github上找到了這個(gè)庫(kù) opencore-amr-js ,這個(gè)庫(kù)使用amr.js對(duì)“amr”文件進(jìn)行轉(zhuǎn)碼:AMR.decode(amr) ,再使用AudioContext 對(duì)象進(jìn)行播放即可。各位可去github上看具體實(shí)現(xiàn)和作者的demo,下面是我整理的得小demo,實(shí)現(xiàn)了播放功能和兼容性處理,除了不支持AudioContext 的IE之外,其他桌面端瀏覽器基本都可以正常運(yùn)行,移動(dòng)端兼容性上我沒(méi)有測(cè)試,據(jù)說(shuō)ios端行不通,安卓端可以:
<!DOCTYPE html>
<html>
<head>
<title>AMR decode/encode tests</title>
<meta charset="utf-8">
<script src="amrnb.js" defer></script>
</head>
<body>
<button id="but">Play</button>
<script>
but.onclick = function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', "test.amr");
xhr.responseType = 'blob';
xhr.onload = function() {
readBlob(this.response, function(data) {
playAmrArray(data);
});
};
xhr.onerror = function() {
alert('Failed to fetch ' + url);
};
xhr.send();
}
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
function readBlob(blob, callback) {
var reader = new FileReader();
reader.onload = function(e) {
var data = new Uint8Array(e.target.result);
callback(data);
};
reader.readAsArrayBuffer(blob);
}
function playAmrArray(array) {
var samples = AMR.decode(array);
if(!samples) {
alert('Failed to decode!');
return;
}
playPcm(samples);
}
function playPcm(samples) {
try {
var ctx = new AudioContext();
} catch(e) {
alert('您的瀏覽器暫不支持播放錄音');
return;
}
var src = ctx.createBufferSource();
var buffer = ctx.createBuffer(1, samples.length, 8000);
if(buffer.copyToChannel) {
buffer.copyToChannel(samples, 0, 0);
} else {
var channelBuffer = buffer.getChannelData(0);
channelBuffer.set(samples);
}
src.buffer = buffer;
src.connect(ctx.destination);
src.start();
}
</script>
</body>
</html>
AudioContext相關(guān)以及兼容性請(qǐng)參考: AudioContext
更新:以上只是實(shí)現(xiàn)了簡(jiǎn)單的播放,如果想添加其他功能的話,可以使用wavesurfer-js 來(lái)實(shí)現(xiàn)炫酷的效果:

部分代碼(ps:我們測(cè)試的效果蠻丑的,希望你可以弄得比上面這個(gè)還要漂亮):
var wavesurfer = Object.create(WaveSurfer);
// Init & load audio file
document.addEventListener("DOMContentLoaded", function() {
var options = {
container: document.querySelector("#waveform"),
waveColor: "violet",
progressColor: "purple",
loaderColor: "purple",
cursorColor: "navy"
};
if (location.search.match("scroll")) {
options.minPxPerSec = 100;
options.scrollParent = true;
}
// Init
wavesurfer.init(options);
// Load audio from URL
//wavesurfer.load("example/media/demo.wav");
// Regions
// if (wavesurfer.enableDragSelection) {
// wavesurfer.enableDragSelection({
// color: "rgba(0, 255, 0, 0.1)"
// });
// }
});
// Play at once when ready
// Won't work on iOS until you touch the page
wavesurfer.on("ready", function() {
wavesurfer.play();
});
// Report errors
wavesurfer.on("error", function(err) {
console.error(err);
});
// Do something when the clip is over
wavesurfer.on("finish", function() {
console.log("播放完成");
});
function readBlob(blob, callback) {
var reader = new FileReader();
reader.onload = function(e) {
var data = new Uint8Array(e.target.result);
callback(data);
};
reader.readAsArrayBuffer(blob);
}
function playAmrArray(array) {
var samples = AMR.decode(array);
if (!samples) {
alert("解碼失敗!");
return;
}
wavesurfer.loadAMRDecodedBuffer(samples);
}
function playAmrContent(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.onload = function() {
readBlob(this.response, function(data) {
playAmrArray(data);
});
};
xhr.onerror = function() {
alert("加載資源失敗");
};
xhr.send();
}
/* Progress bar */
document.addEventListener("DOMContentLoaded", function() {
var progressDiv = document.querySelector("#progress-bar");
var progressBar = progressDiv.querySelector(".progress-bar");
var showProgress = function(percent) {
progressDiv.style.display = "block";
progressBar.style.width = percent + "%";
};
var hideProgress = function() {
progressDiv.style.display = "none";
};
wavesurfer.on("loading", showProgress);
wavesurfer.on("ready", hideProgress);
wavesurfer.on("destroy", hideProgress);
wavesurfer.on("error", hideProgress);
var fileId = getQueryStringByName("file");
playAmrContent("/file/download?param=" + JSON.stringify({ id: fileId }));
});