How to decode only part of mp3 for use with WebAudio API?

In my web application, I have a requirement to play part of an mp3 file. This is a local web application, so I'm not interested in downloads, etc. Everything is stored locally.

My use case is as follows:

  • define file to play
  • determine the start and stop of sound
  • upload file [I use BufferLoader]
  • the game

Pretty simple.

Right now, I'm just grabbing an mp3 file, decrypting it in memory for use with the WebAudio API, and playing it back. Unfortunately, due to the fact that mp3 files can be quite long [30 minutes of audio, for example], a decoded file in memory can take up to 900 MB. This is too much to handle.

Is there an option where I could decode only part of the file? How can I determine where to start and how far to go? I can not predict the bitrate, it can be constant, but I would also expect a variable.

Here is an example of what I did: http://tinyurl.com/z9vjy34

Code [I tried to make it as compact as possible):

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); }; 
+5
source share
1 answer

There is no way to stream with decodeAudioData (), you need to use MediaElement with createMediaStreamSource and then run your stuff. decodeAudioData () cannot play to pieces. @ zre00ne And mp3 will be decrypted big !!! Verybig !!!

+1
source

Source: https://habr.com/ru/post/1243148/


All Articles