Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save S-YOU/5ecf0cab254e9823b33c5d1a6e08c040 to your computer and use it in GitHub Desktop.
Save S-YOU/5ecf0cab254e9823b33c5d1a6e08c040 to your computer and use it in GitHub Desktop.
Audio, WebAudio, Blob, ArrayBuffer
  1. Audio(url)
  2. Audio(arraybuffer)
  3. Audio(blob)
  4. WebAudio(arraybuffer)
  5. WebAudio(blob)

Remote audio file access

http://uupaa.net/issues/3/ の結果

操作方法: loadAudio ▶ 画面が緑になるまで待つ ▶ playAudio の順にクリックする

1 2 3 4 5
iOS 6.1 YES FUZZY YES
iOS 7.1 YES YES YES
iOS sim YES YES YES
iOS 8.0 YES YES YES
Mac WebKit 6.0.x YES YES YES
Mac Chrome 39 YES YES YES YES YES
Android Chrome 37 YES FUZZY YES YES

Local audio file access

MBP に簡素なWebサーバを設置(npm install http-server)し http://uupaa.net/issues/3/ と同じテストを行った結果

1 2 3 4 5
iOS 6.1 YES
iOS 7.1 YES YES
iOS sim YES YES
iOS 8.0 YES YES YES YES
Mac WebKit 6.0.x YES YES
Mac Chrome 39 YES YES YES YES YES
Android Chrome 37 YES FUZZY YES YES
  • iOS 6.1 - iPhone 3GS iOS 6.1

  • iOS 7.1 - iPhone 4S iOS 7.1

  • iOS sim - iPhone 7.x simulator

  • iOS 8.0 - iPhone 5 iOS 8.0 GM Seed

  • Mac WebKit 6.0.x - Mac Book Pro + WebKit 6.0.x nightly

  • Mac Chrome 39 - Mac Book Pro + Chrome canary 39 (64 bit)

  • Android Chrome 37 - Nexus 7 (2012) Chrome for Android 37

  • YES - [loadAudio] ▶ 待つ ▶ 緑になる ▶ [playAudio] で再生が始まる

  • 空白 - [loadAudio] ▶ 待つ ▶ 変化がない ▶ [playAudio] で再生も始まらない

  • FUZZY - [loadAudio] ▶ 待つ ▶ 変化がない ▶ [playAudio] で突然再生が始まる
    canplay や progress イベントが信用できない

雑感

  • <audio>
    • サーバ/Proxyの設定によっては <audio> が機能しないケースがある(よくある)
    • iOS デバイスはバージョンアップで <audio> の実装が密かに変化している
  • WebAudio API は、Blob または ArrayBuffer の両方で利用可能 (iOS 6.x を除く)
  • iPhone 3GS は 256RAM のため、Web Audio は使わないほうがよい(複数ファイルを扱うとメモリ不足で落ちることも)
  • Mac Chrome が優秀なため、全てのシチュエーションで再生できてしまうが、それを鵜呑みにすると、モバイルデバイスで再生されずドハマりする
    • Audio/WebAudio 周りの実装は、必ず実機を用意し、実際の動作を確認しながら行うこと
  • Tegra 2 などの NEON非搭載のAndroid端末では window.AudioContext がなく、Web Audio は利用できない。あきらメロン
<!DOCTYPE html><html><head><title>test</title>
<meta name="viewport" content="width=device-width, user-scalable=no">
<meta charset="utf-8"></head><body>

<p>1. Audio(url)</p>
<input type="button" value="loadAudio" onclick="loadAudio(0)"></input>
<input type="button" value="playAudio" onclick="playAudio(0)"></input>
<input type="button" value="stopAudio" onclick="stopAudio(0)"></input>
<br />
<br />
<p>2. Audio(arraybuffer)</p>
<input type="button" value="loadAudio" onclick="loadAudio(1)"></input>
<input type="button" value="playAudio" onclick="playAudio(1)"></input>
<input type="button" value="stopAudio" onclick="stopAudio(1)"></input>
<br />
<br />
<p>3. Audio(blob)</p>
<input type="button" value="loadAudio" onclick="loadAudio(2)"></input>
<input type="button" value="playAudio" onclick="playAudio(2)"></input>
<input type="button" value="stopAudio" onclick="stopAudio(2)"></input>
<br />
<br />
<p>4. WebAudio(arraybuffer)</p>
<input type="button" value="loadAudio" onclick="loadAudio(3)"></input>
<input type="button" value="playAudio" onclick="playAudio(3)"></input>
<input type="button" value="stopAudio" onclick="stopAudio(3)"></input>
<br />
<br />
<p>5. WebAudio(blob)</p>
<input type="button" value="loadAudio" onclick="loadAudio(4)"></input>
<input type="button" value="playAudio" onclick="playAudio(4)"></input>
<input type="button" value="stopAudio" onclick="stopAudio(4)"></input>
<br />


<script>
var url1 = "./game.m4a";
var stock = {
        0: { node: null, nodeType: "audio",    url: url1, responseType: "",            mime: "", size: 0, data: null, sound: { canplay: false } },
        1: { node: null, nodeType: "audio",    url: url1, responseType: "arraybuffer", mime: "", size: 0, data: null, sound: { canplay: false } },
        2: { node: null, nodeType: "audio",    url: url1, responseType: "blob",        mime: "", size: 0, data: null, sound: { canplay: false } },
        3: { node: null, nodeType: "webaudio", url: url1, responseType: "arraybuffer", mime: "", size: 0, data: null, sound: { buffer: null, source: null } },
        4: { node: null, nodeType: "webaudio", url: url1, responseType: "blob",        mime: "", size: 0, data: null, sound: { buffer: null, source: null } },
    };
var _AudioContext = window.AudioContext ||
                    window.webkitAudioContext;
var webAudioContext = _AudioContext ? new _AudioContext()
                                    : null;

function loadAudio(n) {
    var target = stock[n];

    if (target.responseType) {
        _download(target.url, target.responseType, function(data, mime, size) {
            target.data = data; // blob or arraybuffer
            target.mime = mime;
            target.size = size;
            _downloaded(n);
        });
    } else {
        _downloaded(n);
    }
}

function _download(url, responseType, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
        callback(xhr.response,
                 xhr.getResponseHeader("content-type"),
                 parseInt(xhr.getResponseHeader("content-length")) || 0);
    };
    xhr.responseType = responseType;
    xhr.open("GET", url);
    xhr.send();
}

function _downloaded(n) {
    var target = stock[n];
    var data = target.data;

    if (target.nodeType === "audio") {
        var bloburl = "";
        if (data instanceof ArrayBuffer) {
            bloburl = URL.createObjectURL( new Blob([data], { type: target.mime /* "audio/mp4" */ }) );
        } else if (data instanceof Blob) {
            bloburl = URL.createObjectURL(data);
        } else {
            bloburl = target.url;
        }
        target.node = new Audio();
        target.node.src = bloburl;
        target.node.volume = 0.2;
        target.node.addEventListener("progress", handleEvent, false);
        target.node.addEventListener("canplay", handleEvent, false);
        target.node.load();
    } else {
        if (data instanceof ArrayBuffer) {
            webAudioContext.decodeAudioData(data, function(decodedBuffer) {
                target.sound.buffer = decodedBuffer;
                _ready();
            });
        } else if (data instanceof Blob) {
            var reader = new FileReader();
            reader.onloadend = function() {
                webAudioContext.decodeAudioData(reader.result, function(decodedBuffer) {
                    target.sound.buffer = decodedBuffer;
                    _ready();
                });
            };
            reader.readAsArrayBuffer(data);
        }
    }

    function handleEvent(event) {
        var duration = target.node.duration;

        switch (event.type) {
        case "canplay":
            target.node.removeEventListener("canplay", handleEvent, false);
            target.sound.canplay = true;
            break;
        case "progress":
            break;
        }
        if (duration > 0 && target.sound.canplay) {
            target.node.removeEventListener("progress", handleEvent, false);
            _ready();
        }
    }
}

function playAudio(n) {
    var target = stock[n];

    if (target.nodeType === "audio") {
        target.node.play();
    } else if (target.nodeType === "webaudio") {
        stopAudio(n);
        target.sound.source = webAudioContext.createBufferSource();
        target.sound.source.buffer = target.sound.buffer;
        target.sound.source.connect(webAudioContext.destination);
        if (target.sound.source.start) {
            target.sound.source.start(0);
        } else {
            target.sound.source.noteOn(0); // [iOS 6.x]
        }
    }
}

function stopAudio(n) {
    var target = stock[n];

    if (target.nodeType === "audio") {
        if (target.node) {
            target.node.pause();
        }
    } else if (target.nodeType === "webaudio") {
        if (target.sound.source) {
            if (target.sound.source.stop) {
                target.sound.source.stop(0);
            } else {
                target.sound.source.noteOff(0); // [iOS 6.x]
            }
            target.sound.source = null;
        }
    }
}

var lime = 80;
function _ready() {
    lime += 32;
    document.body.style.cssText = "background-color: rgb(0, " + lime + ", 0)";
}

</script>
</body></html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment