如何只解码部分mp3用于WebAudio API?

时间:2016-02-16 15:40:17

标签: javascript web-audio

在我的网络应用程序中,我需要播放部分mp3文件。这是一个本地Web应用程序,所以我不关心下载等,一切都存储在本地。

我的用例如下:

  • 确定要播放的文件
  • 确定声音的开始和停止
  • 加载文件[我使用BufferLoader]
  • 播放

非常简单。

现在我只是抓住mp3文件,在内存中解码它以便与WebAudio API一起使用,并播放它。 不幸的是,因为mp3文件可能会很长[例如30分钟的音频],内存中的解码文件最多可能需要900MB。这有点太难处理了。

有没有选项,我只能解码部分文件?我怎么能找到从哪里开始以及走多远? 我不能指望比特率,它可以是常数,但我也期望变量。

以下是我所做的一个例子: http://tinyurl.com/z9vjy34

代码[我试图让它尽可能紧凑]:

var MediaPlayerAudioContext = window.AudioContext || window.webkitAudioContext;

var MediaPlayer = function () {
    this.mediaPlayerAudioContext = new MediaPlayerAudioContext();
    this.currentTextItem = 0;
    this.playing = false; 
    this.active = false;
    this.currentPage = null;
    this.currentAudioTrack = 0;
};

MediaPlayer.prototype.setPageNumber = function (page_number) {
    this.pageTotalNumber = page_number
};

MediaPlayer.prototype.generateAudioTracks = function () {
    var audioTracks = [];
    var currentBegin;
    var currentEnd;
    var currentPath;
    audioTracks[0] = {
        begin: 4.300,
        end: 10.000,
        path: "example.mp3"
    };
    this.currentPageAudioTracks = audioTracks;
};

MediaPlayer.prototype.show = function () {
    this.mediaPlayerAudioContext = new MediaPlayerAudioContext();
};

MediaPlayer.prototype.hide = function () {
    if (this.playing) {
        this.stop();
    }
    this.mediaPlayerAudioContext = null;
    this.active = false;
};

MediaPlayer.prototype.play = function () {
    this.stopped = false;
    console.trace();

    this.playMediaPlayer();
};

MediaPlayer.prototype.playbackStarted = function() {
    this.playing = true;
};

MediaPlayer.prototype.playMediaPlayer = function () {
    var instance = this;

    var audioTrack = this.currentPageAudioTracks[this.currentAudioTrack];
    var newBufferPath = audioTrack.path;

    if (this.mediaPlayerBufferPath && this.mediaPlayerBufferPath === newBufferPath) {

        this.currentBufferSource = this.mediaPlayerAudioContext.createBufferSource();
        this.currentBufferSource.buffer = this.mediaPlayerBuffer;
        this.currentBufferSource.connect(this.mediaPlayerAudioContext.destination);

        this.currentBufferSource.onended = function () {
            instance.currentBufferSource.disconnect(0);
            instance.audioTrackFinishedPlaying()
        };

        this.playing = true;
        this.currentBufferSource.start(0, audioTrack.begin, audioTrack.end - audioTrack.begin);

        this.currentAudioStartTimeInAudioContext = this.mediaPlayerAudioContext.currentTime;
        this.currentAudioStartTimeOffset = audioTrack.begin;
        this.currentTrackStartTime = this.mediaPlayerAudioContext.currentTime - (this.currentTrackResumeOffset || 0);
        this.currentTrackResumeOffset = null;

    }
    else {
        function finishedLoading(bufferList) {
            instance.mediaPlayerBuffer = bufferList[0];
            instance.playMediaPlayer();
        }

        if (this.currentBufferSource){
            this.currentBufferSource.disconnect(0);
            this.currentBufferSource.stop(0);
            this.currentBufferSource = null;
        }

        this.mediaPlayerBuffer = null;
        this.mediaPlayerBufferPath = newBufferPath;
        this.bufferLoader = new BufferLoader(this.mediaPlayerAudioContext, [this.mediaPlayerBufferPath], finishedLoading);
        this.bufferLoader.load();
    }
};

MediaPlayer.prototype.stop = function () {
    this.stopped = true;
    if (this.currentBufferSource) {
        this.currentBufferSource.onended = null;
        this.currentBufferSource.disconnect(0);
        this.currentBufferSource.stop(0);
        this.currentBufferSource = null;

    }
    this.bufferLoader = null;
    this.mediaPlayerBuffer = null;
    this.mediaPlayerBufferPath = null;
    this.currentTrackStartTime = null;
    this.currentTrackResumeOffset = null;
    this.currentAudioTrack = 0;

    if (this.currentTextTimeout) {
        clearTimeout(this.currentTextTimeout);
        this.textHighlightFinished();
        this.currentTextTimeout = null;
        this.currentTextItem = null;
    }

    this.playing = false;
};

MediaPlayer.prototype.getNumberOfPages = function () {
    return this.pageTotalNumber;
};

MediaPlayer.prototype.playbackFinished = function () {

    this.currentAudioTrack = 0;
    this.playing = false;

};

MediaPlayer.prototype.audioTrackFinishedPlaying = function () {

    this.currentAudioTrack++;

    if (this.currentAudioTrack >= this.currentPageAudioTracks.length) {
        this.playbackFinished();
    } else {
        this.playMediaPlayer();
    }
};

//
//
// Buffered Loader
//
// Class used to get the sound files
//
function BufferLoader(context, urlList, callback) {
    this.context = context;
    this.urlList = urlList;
    this.onload = callback;
    this.bufferList = [];
    this.loadCount = 0;
}

// this allows us to handle media files with embedded artwork/id3 tags
function syncStream(node) { // should be done by api itself. and hopefully will.
    var buf8 = new Uint8Array(node.buf);
    buf8.indexOf = Array.prototype.indexOf;
    var i = node.sync, b = buf8;
    while (1) {
        node.retry++;
        i = b.indexOf(0xFF, i);
        if (i == -1 || (b[i + 1] & 0xE0 == 0xE0 )) break;
        i++;
    }
    if (i != -1) {
        var tmp = node.buf.slice(i); //carefull there it returns copy
        delete(node.buf);
        node.buf = null;
        node.buf = tmp;
        node.sync = i;
        return true;
    }
    return false;
}

BufferLoader.prototype.loadBuffer = function (url, index) {
    // Load buffer asynchronously
    var request = new XMLHttpRequest();
    request.open("GET", url, true);
    request.responseType = "arraybuffer";

    var loader = this;

    function decode(sound) {
        loader.context.decodeAudioData(
                sound.buf,
                function (buffer) {
                    if (!buffer) {
                        alert('error decoding file data');
                        return
                    }
                    loader.bufferList[index] = buffer;
                    if (++loader.loadCount == loader.urlList.length)
                        loader.onload(loader.bufferList);
                },
                function (error) {
                    if (syncStream(sound)) {
                        decode(sound);
                    } else {
                        console.error('decodeAudioData error', error);
                    }
                }
        );
    }

    request.onload = function () {
        // Asynchronously decode the audio file data in request.response
        var sound = {};
        sound.buf = request.response;
        sound.sync = 0;
        sound.retry = 0;
        decode(sound);
    };
    request.onerror = function () {
        alert('BufferLoader: XHR error');
    };
    request.send();
};

BufferLoader.prototype.load = function () {
    for (var i = 0; i < this.urlList.length; ++i)
        this.loadBuffer(this.urlList[i], i);
};

1 个答案:

答案 0 :(得分:1)

无法使用decodeAudioData()进行流式传输,您需要将MediaElement与createMediaStreamSource一起使用并运行您的内容。 decodeAudioData()无法在某个部分流式传输。@ zre00ne和mp3将被解码大!!! Verybig !!!