Reputation: 3265
Can someone please help me on this issue I am a little confused, i am getting wav data from database, and I can manage to play this wav data on the browser using this function :
function playWave(byteArray) {
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var myAudioBuffer = audioCtx.createBuffer(1, byteArray.length, 8000);
var nowBuffering = myAudioBuffer.getChannelData(0);
for (var i = 0; i < byteArray.length; i++) {
nowBuffering[i] = byteArray[i];
}
var source = audioCtx.createBufferSource();
source.buffer = myAudioBuffer;
source.connect(audioCtx.destination);
source.start();
}
Everything works fine, I only need a GUI player to play/pause/stop and eventually draw a spectrum.
First I tried to use the audio tag of HTML5 but you need to put a valid url in src paramater :
<audio controls="controls">
Your browser does not support the <audio> tag.
<source src="../m/example.mp3" />
</audio>
Is it possible to change the src parameter to something like a method where you can put and play your byte array? Is there any player that can handle this kind of situation? I just want to play a wav from database on a player (play/pause/stop) with a certain rate (8000Hz), it seems to be an easy issue, but I found no article or documentation talking about that on the internet. The only players I found on the internet , you need to provide a valid file.
Upvotes: 3
Views: 4658
Reputation: 18556
You should be able to use Blob
after converting the byteArray
correctly. You can then create a blob Object URL and set that as src
on the source
element:
// Create blob from Uint8Array & Object URL.
const blob = new Blob([getByteArray()], { type: 'audio/wav' });
const url = URL.createObjectURL(blob);
// Get DOM elements.
const audio = document.getElementById('audio');
const source = document.getElementById('source');
// Insert blob object URL into audio element & play.
source.src = url;
audio.load();
audio.play();
// Get data from database/server, hardcoded here for simplicity.
function getByteArray() {
const data = [82, 73, 70, 70, 222, 37, 0, 0, 87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 1, 0, 68, 172, 0, 0, 136, 88, 1, 0, 2, 0, 16, 0, 100, 97, 116, 97, 186, 37, 0, 0, 0, 0, 255, 12, 2, 27, 254, 40, 2, 55, 254, 68, 1, 83, 0, 83, 0, 69, 0, 55, 255, 40, 2, 27, 253, 12, 3, 255, 254, 240, 0, 227, 1, 213, 255, 198, 1, 185, 255, 170, 1, 175, 255, 188, 1, 203, 255, 216, 1, 231, 255, 244, 2, 3, 254, 16];
// Convert byteArray into Uint8Array.
return new Uint8Array(data);
}
<audio controls="controls" id="audio" loop>
Your browser does not support the <audio> tag.
<source id="source" src="" type="audio/wav" />
</audio>
āļøš This plays sound on click!
Upvotes: 6
Reputation: 2714
I made an angular controller for audio playback dialog here it is:
App.controller("PlaySoundDialogCtrl", function ($scope, $rootScope, $http, $interval, $mdDialog, $mdMedia, url) {
$scope.url = url;
$scope.error = "";
$scope.audioData = null;
$scope.resampleRate = 44100;
$scope.playing = false;
$scope.source = null;
$scope.buffer = null;
$scope.startedAt = 0;
$scope.pausedAt = 0;
$scope.duration = 0;
$scope.currentTime = 0;
$scope.timeChunks = [];
window.AudioContext = window.AudioContext ||
window.webkitAudioContext ||
window.mozAudioContext ||
window.oAudioContext ||
window.msAudioContext;
$scope.audioCtx = new AudioContext();
$http.get($scope.url, {responseType: "arraybuffer"}).
success(function(data) {
$scope.audioData = data;
$scope.playByteArray(data);
}).
error(function(data, status) {
$scope.error = "errors.sound-error";
});
$scope.playByteArray = function(byteArray) {
var data = new DataView(byteArray);
var audio = new Int16Array(data.byteLength / Int16Array.BYTES_PER_ELEMENT);
var len = audio.length;
for(var jj = 0; jj < len; ++jj) {
audio[jj] = data.getInt16(jj * Int16Array.BYTES_PER_ELEMENT, true);
}
var mono = new Float32Array(audio.length);
var channelCounter = 0;
for(var i = 0; i < audio.length; ) {
mono[channelCounter] = (audio[i] > 0 ? audio[i]/32767 : audio[i]/-32768);
i = i+1;
channelCounter++;
}
$scope.buffer = $scope.audioCtx.createBuffer(1, mono.length, $scope.resampleRate);
$scope.buffer.getChannelData(0).set(mono);
$scope.duration = $scope.buffer.duration;
for(var i = 0; i < 20; i++) {
$scope.timeChunks.push(($scope.duration/20)*i);
}
window.setTimeout(function() {$scope.play();}, 100);
}
$scope.play = function() {
var offset = $scope.pausedAt;
$scope.source = $scope.audioCtx.createBufferSource();
$scope.source.connect($scope.audioCtx.destination);
$scope.source.buffer = $scope.buffer;
$scope.source.start(0, offset);
$scope.startedAt = $scope.audioCtx.currentTime - offset;
$scope.pausedAt = 0;
$scope.playing = true;
$scope.playInterval = $interval(function() {
$scope.currentTime = $scope.getCurrentTime();
if($scope.currentTime > $scope.duration) {
$scope.stop();
$interval.cancel($scope.playInterval);
$scope.currentTime = 0;
}
}, 500);
}
$scope.timeWatch = $scope.$watch("currentTime", function() {
var all = $(".timeRanger").innerWidth();
var leftOffset = Math.floor(((all-30)/$scope.duration)*$scope.currentTime);
if($(".playerTooltip").length) {
$(".playerTooltip")[0].style.setProperty("left", leftOffset+"px", "important");
}
});
$scope.stop = function() {
if($scope.source != null) {
$scope.source.disconnect();
$scope.source.stop(0);
$scope.source = null;
}
$scope.pausedAt = 0;
$scope.startedAt = 0;
$scope.playing = false;
if(angular.isDefined($scope.playInterval)) $interval.cancel($scope.playInterval);
}
$scope.pause = function() {
var elapsed = $scope.audioCtx.currentTime - $scope.startedAt;
$scope.stop();
$scope.pausedAt = elapsed;
}
$scope.jump = function() {
$scope.pause();
$scope.pausedAt = $scope.currentTime;
$scope.play();
}
$scope.getCurrentTime = function() {
if($scope.pausedAt) {
return $scope.pausedAt;
}
if($scope.startedAt) {
return $scope.audioCtx.currentTime - $scope.startedAt;
}
return 0;
}
$scope.cancel = function() {
$scope.stop();
$mdDialog.cancel();
}
$scope.stopInterval = function() {
if(angular.isDefined($scope.playInterval)) $interval.cancel($scope.playInterval);
}
$scope.$on('$destroy',function() {
if(angular.isDefined($scope.playInterval)) $interval.cancel($scope.playInterval);
if(angular.isDefined($scope.timeWatch)) $scope.timeWatch();
});
}).filter('secToTime', function() {
return function(item) {
var minutes = Math.floor(item/60);
item -= minutes*60;
item = Math.floor(item);
if(item < 10) item = "0"+item;
return minutes+":"+item;
};
});
and the view part:
<div class="row text-center">
{{currentTime | secToTime}}
<button type="button" class="btn btn-primary" ng-click="pause()" ng-show="playing">Pause</button>
<button type="button" class="btn btn-primary" ng-click="play()" ng-show="!playing">Play</button><br />
<div class="timeRanger">
<input type="range" min="0" max="{{duration}}" step="1" ng-mousedown="stopInterval()" ng-mouseup="jump()" ng-model="currentTime" />
<div class="tooltip playerTooltip top fade in" tooltip-animation-class="fade" style="top: -33px; left: 0;">
<div class="tooltip-arrow"></div>
<div class="tooltip-inner">{{currentTime | secToTime}}</div>
</div>
</div>
<!--<button type="button" class="btn btn-primary" ng-click="jump(t)" ng-repeat="t in timeChunks">{{t | secToTime}}</button>-->
</div>
Upvotes: -1