2

I am working on an Ionic / Cordova app which involves loading three minute audio clips (mp3) from a remote server and playing them.

I have followed this tutorial - http://www.html5rocks.com/en/tutorials/webaudio/intro/ - and the loading and playing works fine.

However I want to store / cache the loaded mp3 locally so that the user only has to download it once and the app can work offline. The files are approx 750 KB. In the example below the file is only 74 KB.

Elsewhere in the app I am using Mozilla's localForage (via [ngular-localForage) which specifically states it can be used to store ArrayBuffers - http://mozilla.github.io/localForage/#setitem

However I can't seem to retrieve the audio in a format I can play... I suspect I'm storing the wrong thing - I don't really understand what an ArrayBuffer is!

Anyway here is my basic code (at the moment I'm using a local file). First load the audio plays correctly, second load I get an error (see below):

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

function loadDingSound() {

    // attempt to retrieve stored file
    $localForage.getItem('ding').then(function (response) {

        if (response) {

            console.log('playing audio from localForage');

            console.log(response);

            context.decodeAudioData(response, function(buffer) {
                playSound(buffer);
            });

        } else {

            var request = new XMLHttpRequest();

            request.open('GET', 'audio/ding.mp3', true); // loading local file for now
            request.responseType = 'arraybuffer';

            // Decode asynchronously
            request.onload = function() {

                $localForage.setItem('ding', request.response).then(function () {

                    context.decodeAudioData(request.response, function(buffer) {

                        playSound(buffer);

                    });

                });

            };
            request.send();
        }

    });

}

function playSound(buffer) {

    var source = context.createBufferSource(); // creates a sound source
    source.buffer = buffer;                    // tell the source which sound to play
    source.connect(context.destination);       // connect the source to the context's destination (the speakers)
    source.start(0);                           // play the source now
}

// init
loadDingSound();

And the response I get in the console is as follows:

Object {} // line 13
Error: Failed to execute 'decodeAudioData' on 'AudioContext': invalid ArrayBuffer for audioData. // line 15

At the moment I've only been testing this in Chrome, not the Cordova app.

What am I doing wrong? Or is there is a better way to do this? Perhaps I could save the MP3 to the file system instead...?

4

1 回答 1

0

无需执行两次从 mp3 压缩二进制到 PCM 原始浮点类型数组的转换,因此避免两次调用 decodeAudioData。另外,让我们问一个更简单的问题,所以暂时忽略 localForage - 只需将对原始音频缓冲区的引用保存到它自己的 var 中以供以后重播:

<html>
<head>
    <title>Test</title>
</head>

<body>

    <script>

window.AudioContext = window.AudioContext || window.webkitAudioContext;
var audio_context = new AudioContext();

var stored_buffer = null;

function loadDingSound() {

    if (stored_buffer) {  // attempt to retrieve stored file

        console.log('playing audio from localForage');

        playSound(stored_buffer);

    } else {

        var request = new XMLHttpRequest();

        var audio_url = "audio/ding.mp3"; // relative path to reach mp3 file

        request.open('GET', audio_url, true); // loading local file for now
        request.responseType = 'arraybuffer';

        // Decode asynchronously
        request.onload = function() {

            audio_context.decodeAudioData(request.response, function(buffer) {

                stored_buffer = buffer; // store buffer for replay later

                playSound(buffer);

            });
        };
        request.send();
    }
}

function playSound(buffer) {

    var source = audio_context.createBufferSource(); // creates a sound source
    source.buffer = buffer;                    // tell the source which sound to play
    source.connect(audio_context.destination);       // connect source to speakers
    source.start(0);                           // play the source now
}

// init
loadDingSound();


    </script>

      <body onkeypress="loadDingSound()">

    <button onclick="loadDingSound()">play_again</button>

</body>
</html>

一旦你对此感到满意,然后系上 localForage - 应该很快

于 2015-01-12T22:47:18.753 回答