使用amr.js播放"amr"格式的錄音文件

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 }));
});
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容