├── .gitignore ├── run.sh ├── chunks ├── xa ├── xb ├── xc ├── xd ├── xe ├── xf ├── xg ├── xh ├── xi ├── xj ├── xk ├── xl ├── xm ├── xn ├── xo ├── xp ├── xq ├── xr ├── xs ├── xt ├── xu ├── xv ├── xw ├── xx ├── xy └── .DS_Store ├── README.md ├── LICENSE ├── visualiser.js ├── eventtarget.js ├── index.html └── mp3chunksplayer.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python -m SimpleHTTPServer 8282 3 | -------------------------------------------------------------------------------- /chunks/xa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xa -------------------------------------------------------------------------------- /chunks/xb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xb -------------------------------------------------------------------------------- /chunks/xc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xc -------------------------------------------------------------------------------- /chunks/xd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xd -------------------------------------------------------------------------------- /chunks/xe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xe -------------------------------------------------------------------------------- /chunks/xf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xf -------------------------------------------------------------------------------- /chunks/xg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xg -------------------------------------------------------------------------------- /chunks/xh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xh -------------------------------------------------------------------------------- /chunks/xi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xi -------------------------------------------------------------------------------- /chunks/xj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xj -------------------------------------------------------------------------------- /chunks/xk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xk -------------------------------------------------------------------------------- /chunks/xl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xl -------------------------------------------------------------------------------- /chunks/xm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xm -------------------------------------------------------------------------------- /chunks/xn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xn -------------------------------------------------------------------------------- /chunks/xo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xo -------------------------------------------------------------------------------- /chunks/xp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xp -------------------------------------------------------------------------------- /chunks/xq: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xq -------------------------------------------------------------------------------- /chunks/xr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xr -------------------------------------------------------------------------------- /chunks/xs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xs -------------------------------------------------------------------------------- /chunks/xt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xt -------------------------------------------------------------------------------- /chunks/xu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xu -------------------------------------------------------------------------------- /chunks/xv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xv -------------------------------------------------------------------------------- /chunks/xw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xw -------------------------------------------------------------------------------- /chunks/xx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xx -------------------------------------------------------------------------------- /chunks/xy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/xy -------------------------------------------------------------------------------- /chunks/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/72lions/PlayingChunkedMP3-WebAudioAPI/HEAD/chunks/.DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Playing a chunked MP3 with the Web Audio API 2 | ============================= 3 | 4 | Play a chunked MP3 with the Web Audio API without having to wait for all the pieces to be loaded. 5 | 6 | In this proof of concept **(currently only tested and working in Chrome 24+)** I've split the mp3 in 25 parts by using the unix split command. The moment the first part is loaded then the playback starts immediately and it loads the second part. 7 | When the second part is loaded then then I create a new AudioBuffer by combining the old and the new, and I change the buffer of the AudioSourceNode with the new one. At that point I start playing again from the new AudioBuffer. 8 | 9 | A lot of thanks to Paul (@aerotwist) for his suggestions and to Theo for letting me use his awesome track Breathe In. 10 | 11 | #Example 12 | 13 | http://72lions.github.com/PlayingChunkedMP3-WebAudioAPI 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Theodoros Tsiridis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /visualiser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The visualiser class 3 | * 4 | * @constructor 5 | * @class Visualiser 6 | * @param {String} id The id of the canvas element. 7 | */ 8 | var Visualiser = function(id) { 9 | 10 | EventTarget.call(this); 11 | 12 | /** 13 | * The width of the canvas 14 | * 15 | * @private 16 | * @type {Number} 17 | */ 18 | var CANVAS_WIDTH = 1024; 19 | 20 | /** 21 | * The height of the canvas 22 | * 23 | * @private 24 | * @type {Number} 25 | */ 26 | var CANVAS_HEIGHT = 300; 27 | 28 | /** 29 | * The canvas dom element 30 | * 31 | * @private 32 | * @type {DOMElement} 33 | */ 34 | var _canvas; 35 | 36 | /** 37 | * The context of the canvas element 38 | * 39 | * @private 40 | * @type {CanvasRenderingContext2D} 41 | */ 42 | var _ctx; 43 | 44 | /** 45 | * The gradient 46 | * 47 | * @type {CanvasGradient} 48 | */ 49 | var _gradient; 50 | 51 | /** 52 | * Draws on the canvas based on the data 53 | * 54 | * @param {Array} data An array containing all the data. 55 | * @return {Visualiser} Returns a reference to this instance. 56 | */ 57 | this.draw = function(data) { 58 | _ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); 59 | _ctx.fillStyle = _gradient; 60 | for (var i = 0, len = data.length; i < len; i += 1) { 61 | var value = data[i]; 62 | _ctx.fillRect(i * 3, CANVAS_HEIGHT - value, 1, CANVAS_HEIGHT); 63 | } 64 | 65 | return this; 66 | }; 67 | 68 | /** 69 | * Initializes the class by loading the first chunk 70 | * 71 | * @return {Visualiser} Returns a reference to this instance. 72 | */ 73 | this.init = function() { 74 | console.log('Visualiser initialized...'); 75 | 76 | _canvas = document.getElementById(id); 77 | _canvas.width = CANVAS_WIDTH; 78 | _canvas.height = CANVAS_HEIGHT; 79 | _ctx = _canvas.getContext('2d'); 80 | 81 | _gradient = _ctx.createLinearGradient(0, 0, 0, 300); 82 | 83 | _gradient.addColorStop(1, '#ff0000'); 84 | _gradient.addColorStop(0.75, '#ff0000'); 85 | _gradient.addColorStop(0.25, '#ff0000'); 86 | _gradient.addColorStop(0, '#f3f3f3'); 87 | 88 | return this; 89 | }; 90 | }; 91 | -------------------------------------------------------------------------------- /eventtarget.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Event target is used as a mixin so that the classes can 3 | * support dispatch events and add events commands 4 | * 5 | * @class EventTarget 6 | * @constructor 7 | * 8 | */ 9 | var EventTarget = (function() { 10 | 11 | /** 12 | * The object that will hold all the event listeners 13 | * 14 | * @private 15 | * @type {Object} 16 | */ 17 | this._listeners = {}; 18 | 19 | /** 20 | * Registers an event 21 | * 22 | * @param {String} type The event type. 23 | * @param {Function} listener The callback function. 24 | * @param {Object} ctx The context. 25 | * @param {Number} priority The priority. 26 | */ 27 | this.bind = function(type, listener, ctx, priority) { 28 | 29 | if (typeof priority === 'undefined') { 30 | priority = 0; 31 | } 32 | 33 | if (typeof listener !== 'undefined' && listener !== null) { 34 | 35 | var obj = {callback: listener, context: ctx, priority: priority}; 36 | var exists = false; 37 | var events; 38 | 39 | if (this._listeners[type] === undefined) { 40 | this._listeners[type] = []; 41 | } 42 | 43 | events = this._listeners[type]; 44 | 45 | for (var i = 0; i < events.length; i++) { 46 | 47 | if (events[i].callback === listener && events[i].context === ctx) { 48 | exists = true; 49 | break; 50 | } 51 | 52 | } 53 | 54 | if (exists === false) { 55 | this._listeners[type].push(obj); 56 | this._listeners[type].sort(_sortByPriorityDesc); 57 | } 58 | 59 | } 60 | 61 | }; 62 | 63 | /** 64 | * Custom sorting function based on priority descending 65 | * 66 | * @private 67 | * @function 68 | * @param {Object} a The first element to compare each priority. 69 | * @param {Object} b The second element to compare each priority. 70 | * @return {Number} The result of the comparison. 71 | */ 72 | var _sortByPriorityDesc = function(a, b) { 73 | return (b.priority - a.priority); //causes an array to be sorted numerically and ascending 74 | }; 75 | 76 | /** 77 | * Dispatches an event 78 | * 79 | * @param {String} type The event type. 80 | * @param {Object} params The object. 81 | * @param {Object} extra Any extra arguments. 82 | */ 83 | this.trigger = function(type, params, extra) { 84 | var events = this._listeners[type]; 85 | params = params || {}; 86 | if (typeof events !== 'undefined') { 87 | for (var i = 0; i < events.length; i++) { 88 | var event = events[i]; 89 | event.callback.call(event.context, {type: type, params: params, extra: extra || {}}); 90 | } 91 | } 92 | 93 | }; 94 | 95 | /** 96 | * Removes an event 97 | * 98 | * @param {String} type The event type. 99 | * @param {Function} listener The callback function. 100 | * @param {Object} ctx The context. 101 | * 102 | */ 103 | this.unbind = function(type, listener, ctx) { 104 | var index = -1; 105 | var events = this._listeners[type]; 106 | if (typeof listener !== 'undefined') { 107 | if (typeof events !== 'undefined') { 108 | for (var i = 0; i < events.length; i++) { 109 | if (events[i].callback === listener && events[i].context === ctx) { 110 | index = i; 111 | break; 112 | } 113 | } 114 | 115 | if (index !== - 1) { 116 | this._listeners[type].splice(index, 1); 117 | } 118 | } 119 | } else { 120 | this._listeners[type] = []; 121 | } 122 | }; 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
60 | In this proof of concept (currently only tested and working in Chrome 24+) I've split the mp3 in 25 parts by using the unix split command.
61 | The moment the first part is loaded then the playback starts immediately and it loads the second part.
62 | When the second part is loaded then I create a new AudioBuffer by combining the old and the new,
63 | and I change the buffer of the AudioSourceNode with the new one. At that point I start playing again
64 | from the new AudioBuffer.
65 |
66 | A lot of thanks to Paul (@aerotwist) for his suggestions and to Theo for letting me use his awesome track Breathe In.
67 |
Download source or see the progject on Github
69 | Debug: 70 |