├── README.md ├── amrnb.js └── amrplayer.js /README.md: -------------------------------------------------------------------------------- 1 | amr-player 2 | play remote amr format audio with JavaScript 3 | 4 | inpired by https://github.com/yxl/opencore-amr-js 5 | 6 | -- AmrPlayer -- 7 | 8 | params: 9 | >amr_url 10 | >download_success_cb (optional) 11 | >download_progress_cb (optional) 12 | 13 | props: 14 | >bool canPlay 15 | >bool isPlaying 16 | 17 | methods: 18 | >play() 19 | >pause() 20 | >toggle() // play() when paused or pause() when playing 21 | >endWith(callback) // optional, fire callback with ended event 22 | 23 | usage: 24 | 25 | 26 | 27 | var player = new AmrPlayer('http://xxx.com/xxx.amr'); 28 | player.endWith(function(){ console.log( xxx ) }); 29 | player.play(); 30 | // or player.pause(); 31 | // or player.toggle(); 32 | -------------------------------------------------------------------------------- /amrplayer.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * -- AmrPlayer -- 4 | * params: 5 | * 1. amr_url 6 | * 2. download_success_cb (optional) 7 | * 3. download_progress_cb (optional) 8 | * props: 9 | * 1. bool canPlay 10 | * 2. bool isPlaying 11 | * methods: 12 | * 1. play() 13 | * 2. pause() 14 | * 3. toggle() // play() when paused or pause() when playing 15 | * 3. endWith(callback) // fire callback with ended event 16 | * */ 17 | var AmrPlayer = function(amr_url, download_success_cb, download_progress_cb){ 18 | this.init(amr_url, download_success_cb, download_progress_cb); 19 | }; 20 | AmrPlayer.prototype = { 21 | init: function(amr_url, download_success_cb, download_progress_cb){ 22 | this.audioContext = null; 23 | this.bufferSource = null; 24 | this.blob = null; 25 | this.canPlay = false; 26 | this.isPlaying = false; 27 | var cnt = 0; 28 | this.ended_cb = function(){ 29 | if(cnt === 0){ 30 | cnt++; 31 | var msg = "AmrPlayer ended callback\n"; 32 | msg += "usage:\n"; 33 | msg += "var player = new AmrPlayer('http://xxx.com/xxx.amr');\n"; 34 | msg += "player.endedWith( function(){ xxx } );"; 35 | console.info(msg); 36 | } 37 | }; 38 | this.downloadAmrBlob(amr_url, download_success_cb, download_progress_cb); 39 | }, 40 | downloadAmrBlob: function(amr_url, download_success_cb, download_progress_cb){ 41 | var self = this; 42 | var xhr = new XMLHttpRequest(); 43 | xhr.open('GET', amr_url); 44 | xhr.responseType = 'blob'; 45 | xhr.onreadystatechange = function(e) { 46 | if ( xhr.readyState == 4 && xhr.status == 200 ) { 47 | self.blob = new Blob([xhr.response], {type: 'audio/mpeg'}); 48 | self.genPLayer(); 49 | self.canPlay = true; 50 | download_success_cb && download_success_cb(); 51 | } 52 | }; 53 | xhr.onprogress = function(e){ 54 | if(e.lengthComputable){ 55 | download_progress_cb && download_progress_cb(e); 56 | } 57 | }; 58 | xhr.send(); 59 | }, 60 | genPLayer: function(){ 61 | var self = this; 62 | this.isPlaying = false; 63 | this.readBlob(this.blob, function(data){ 64 | self.readAmrArray(data); 65 | }); 66 | }, 67 | readBlob: function(blob, callback) { 68 | var reader = new FileReader(); 69 | reader.onload = function(e) { 70 | var data = new Uint8Array(e.target.result); 71 | callback(data); 72 | }; 73 | reader.readAsArrayBuffer(blob); 74 | }, 75 | readAmrArray: function(array) { 76 | var samples = AMR.decode(array); 77 | if (!samples) { 78 | alert('Failed to decode!'); 79 | return; 80 | } 81 | this.readPcm(samples); 82 | }, 83 | readPcm: function(samples) { 84 | var self = this; 85 | var ctx = this.getAudioContext(); 86 | this.bufferSource = ctx.createBufferSource(); 87 | var buffer = ctx.createBuffer(1, samples.length, 8000); 88 | if (buffer.copyToChannel) { 89 | buffer.copyToChannel(samples, 0, 0) 90 | } else { 91 | var channelBuffer = buffer.getChannelData(0); 92 | channelBuffer.set(samples); 93 | } 94 | this.bufferSource.buffer = buffer; 95 | this.bufferSource.connect(ctx.destination); 96 | this.bufferSource.onended = function(){ 97 | self.ended_cb && self.ended_cb(); 98 | self.genPLayer(); 99 | }; 100 | }, 101 | getAudioContext: function(){ 102 | if (!this.audioContext) { 103 | if(window.AudioContext) { 104 | this.audioContext = new AudioContext(); 105 | } else { 106 | this.audioContext = new window.webkitAudioContext(); 107 | } 108 | } 109 | return this.audioContext; 110 | }, 111 | play: function(){ 112 | if( !this.isPlaying && this.canPlay ){ 113 | this.bufferSource.start(); 114 | this.isPlaying = true; 115 | } 116 | else{ 117 | this.warn('can not play now'); 118 | } 119 | }, 120 | pause: function(){ 121 | if( this.isPlaying && this.canPlay ) { 122 | this.bufferSource.stop(); 123 | this.genPLayer(); 124 | } 125 | else{ 126 | this.warn('can not pause now'); 127 | } 128 | }, 129 | toggle: function(){ 130 | this.isPlaying ? this.pause() : this.play(); 131 | }, 132 | endedWith: function(cb){ 133 | this.ended_cb = cb; 134 | }, 135 | warn: function(msg){ 136 | console.warn(msg); 137 | } 138 | }; --------------------------------------------------------------------------------