audio.play() works in Chrome but not Safari - javascript

I know there are questions similar to this on this site that have been answered, but I haven't found any that fix this issue which is why I'm making a post.
Here is what I have tried:
Listening for events "canplay", "loadedmetadata", "canplaythrough"
Doing audio.load() on initialization
Setting audio.preload to "auto"
Testing on Chrome (it works perfectly in this browser!)
Appending the audio element to document.body on initialization
When I try to play() without the eventListener (which also doesn't work), the console says: Unhandled Promise Rejection: NotSupportedError: The operation is not supported.
Am I missing something?
The code:
var AUDIO = {
create: function(name, file, type) {
AUDIO.library[name] = {};
AUDIO.library[name].type = type;
AUDIO.library[name].audio = new Audio();
AUDIO.library[name].audio.preload = "auto";
if (type=="music") {
AUDIO.library[name].audio.loop = true;
}
AUDIO.library[name].audio.src = file;
AUDIO.library[name].audio.load();
},
play: function(name, fadeout_playing) {
if (fadeout_playing) {
for (let i=0; i<AUDIO.playing.length; i++) {
let n = AUDIO.playing[i];
AUDIO.fadeout(n);
}
}
let a = AUDIO.library[name].audio;
a.currentTime = 0;
a.addEventListener("canplaythrough", function() {
console.log("you are listening to: "+ name);
AUDIO.playing.push(name);
a.play();
}, false);
},
fadeout: function(name) {
var sound = AUDIO.library[name].audio;
var fade = setInterval(function() {
if (sound.volume >= 0.1) {
sound.volume -= 0.1;
sound.volume = sound.volume.toFixed(1);
}
if (sound.volume <= 0) {
sound.volume = 0;
let index = AUDIO.playing.indexOf(name);
AUDIO.playing.splice(index, 1);
clearInterval(fade);
}
}, 200);
},
library: {},
playing: [],
};
AUDIO.create("act1", "music/chillelectric.mp3", "music");
//this runs when i click a button
function setup() {
...
AUDIO.play("act1");
}
Thanks in advance.

Related

javascript multiple sounds fired on event

I'm working on a simple script, trying to put 2 or more sounds in html using js and fire them on certain events (from a game) with callbacks.
I managed to get it working with 1 sound, but it fails when i try to add the second one or more, any ideas?
var audio = new Audio("audio.wav");
audio.addEventListener('ended', function()
{
this.currentTime = 0;
this.play();
}, false);
and callbacks
{
Start: function()
{
audio.play();
},
Stop: function()
{
audio.pause();
},
};
Here's an implementation adapted from a tutorial I found.
// A sound pool to use for the sound effects.
// http://blog.sklambert.com/html5-canvas-game-html5-audio-and-finishing-touches/#adding-html-audio
function SoundPool(filename, volume, maxSize) {
var pool = [];
this.pool = pool;
var currSound = 0;
var that = this;
// Populates the pool array with the given sound.
for (var i = 0; i < maxSize; i++) {
var sound = new Audio(filename);
sound.volume = volume;
pool[i] = sound;
}
this.setVolume = function setVolume(volume) {
for (var i = 0; i < that.pool.length; i++) {
that.pool[i].volume = volume;
}
};
this.mute = function mute(state) {
for (var i = 0; i < that.pool.length; i++) {
// State: toggle, true or false.
if (typeof state == "undefined")
that.pool[i].muted = !that.pool[i].muted;
else
that.pool[i].muted = state;
}
};
// Plays a sound.
this.play = function () {
if (pool[currSound].currentTime == 0 || pool[currSound].ended) {
pool[currSound].play();
}
currSound = (currSound + 1) % maxSize;
};
}
var laserSound = new SoundPool("sound/effects/laser.wav", 0.5, 300);
laserSound.play();

An issue with CSS animations and JS classList.add

I'm writing a private 'framework' of sorts in order to have some basic effects without the need of jQuery.
I've just written some code for fade-ins/outs, and basically what I do is add/remove classes, and check for animation support.
Here's the JavaScript part (please, check mainly the fadeIn, fadeOut properties):
var effs = {
checkSupport: (function() {
var init = true;
var supports = false;
return function(el) {
if (init) {
var animationstring = 'animation',
keyframeprefix = '',
domPrefixes = 'Webkit Moz O ms Khtml'.split(' '),
pfx = '';
if (el.style.animationName !== undefined) {
supports = true;
}
if (supports === false) {
for (var i = 0; i < domPrefixes.length; i++) {
if (el.style[domPrefixes[i] + 'AnimationName'] !== undefined) {
pfx = domPrefixes[i];
animationstring = pfx + 'Animation';
keyframeprefix = '-' + pfx.toLowerCase() + '-';
supports = true;
break;
}
}
}
init = false;
}
return supports;
};
}()),
fadeOut: function(el) {
el.style.opacity = 1;
if (this.checkSupport(el)) {
el.classList.remove("fadeIn", "fadeOut");
//Here comes the annoying timeout!
setTimeout(function() {
el.classList.add("fadeOut");
}, 25);
} else {
el.style.opacity = 0;
}
},
fadeIn: function(el) {
el.style.opacity = 0;
if (this.checkSupport(el)) {
el.classList.remove("fadeIn", "fadeOut");
setTimeout(function() {
el.classList.add("fadeIn");
}, 25);
} else {
el.style.opacity = 1;
}
}
};
As you can see, I have had to delay the addition of the animation class, because without that timeout, the animation doesn't show the second time around in Chrome(and other Webkit browsers), when it is called on the same element. It does work in Firefox without the timeout, though.
I also tested this in Midori, and since it's also a Webkit browser, it needs the timeout as well, but with the added annoyance of the delay being even longer than 25ms, for some reason.
Why is this? Is there any other way around that you can think of? I know these methods aren't very compatible with a lot of browsers, but I don't mind the animations not showing in some of them. Thank you.

dojo.connect is not working inside a loop. It only keeps the last time I did it

Hi here is my function
function connectAccordionTabbing() {
for(var i = 0; i < coverageStates.length;i=i+3){
var jsState = coverageStates[i]+coverageStates[i+1];
var firstNode = dijit.getFirstInTabbingOrder(document.getElementById('liabCovBlock_'+jsState));
var lastNode = dijit.getLastInTabbingOrder(document.getElementById('liabCovBlock_'+jsState));
dojo.connect(firstNode, "onkeypress", function(evt) {
setTimeout(function() {
setAccordionTabOffFields(evt,jsState, true);
}, 10);
});
dojo.connect(lastNode, "onkeypress", function(evt) {
setTimeout(function() {
setAccordionTabOffFields(evt,jsState, false);
}, 10);
});
}
}
It loops through and goes through each state and makes the first and last nodes inside a block have connections during the page load. The problem I am having is that when I am on one of those nodes and press a key it calls setAccordionTabOffFields but it thinks my state is the last state even if I was on the third state. It does that in Chrome and IE but in firefox it does not even call the setAccordionTabOffFields.
Any ideas what I may be doing wrong?
Thanks
Try creating a global var to store your dojo events (In your loop, add the dojo connect objects to the global var):
var formEvents = [];
formEvents.push(dojo.connect(dojo.connect(lastNode, "onkeypress", function(evt) {
setTimeout(function() {
setAccordionTabOffFields(evt,jsState, false);
}, 10););
or
function connectAccordionTabbing() {
for(var i = 0; i < coverageStates.length;i=i+3){
var jsState = coverageStates[i]+coverageStates[i+1];
var firstNode = dijit.getFirstInTabbingOrder(document.getElementById('liabCovBlock_'+jsState));
var lastNode = dijit.getLastInTabbingOrder(document.getElementById('liabCovBlock_'+jsState));
formEvents.push(dojo.connect(firstNode, "onkeypress", function(evt) {
setTimeout(function() {
setAccordionTabOffFields(evt,jsState, true);
}, 10);
}););
formEvents.push(dojo.connect(lastNode, "onkeypress", function(evt) {
setTimeout(function() {
setAccordionTabOffFields(evt,jsState, false);
}, 10);
}););
}
}
When cleaning up, don't forget about the global:
//Disconnect Dojo events
for (var x=2;x < formEvents.length;x++) {
var handle = formEvents[x];
dojo.disconnect(handle);
}

How to play songs without refreshing the web page?

I want to play the songs only if the user press the button 'play',
without Waiting to load all the 21 songs.
when I press the button 'play' the page jump up like refreshing ,it is not normal.
what I can do to improve my code.
please try to play the songs in the example site and see the problem.
many thanks.
var Music = {
init:function(){
song = new Audio();
//speaKer = document.getElementById("imgSpeaker");
this.volume = 0.08;
this.isMute = false;
canPlayMP3 = (typeof song.canPlayType === "function" && song.canPlayType("audio/mpeg") !== "");
song.src = canPlayMP3 ? "http://rafih.co.il/wp-content/uploads/2013/07/1.mp3" : "http://rafih.co.il/wp-content/uploads/2013/07/1.ogg";
song.preload = 'auto';
},
/* start:function(){
song.src = canPlayMP3 ? "http://rafih.co.il/wp-content/uploads/2013/07/1.mp3" : "http://rafih.co.il/wp-content/uploads/2013/07/1.ogg";
song.volume = 0.08;
song.autoplay = true;
song.load();
},*/
play: function () {
song.volume = 0.08;
song.autoplay = true;
song.load();
// Music.speaker();
},
stop: function () {
if (!song.ended) {
song.pause();
}
},
next: function () {
if (curr < count) {
curr++;
}else curr = 1;
song.src = canPlayMP3 ? "http://rafih.co.il/wp-content/uploads/2013/07/" + curr + ".mp3" : "http://rafih.co.il/wp-content/uploads/2013/07/" + curr + ".ogg";
},
};
function load() {
Music.init();
}
You need to return false from the play buttons event handler, to prevent the page from scrolling to the top.
Change:
<div id="play" onclick='Music.play()'>
To:
<div id="play" onclick='Music.play(); return false;'>
That's because of the anchor in your link, such as href="#". A very simple fix would be to add return false; in to your inline handler attribute, such as onclick="Music.play(); return false;", however that isin't a good approach and is considered a bad practice.
Instead you could add your handlers programmatically, it will also make your code more modular and testable:
var Music = {
init: function (options) {
var playBtn = this.playBtn = document.querySelector(options.playBtn);
playBtn.addEventListener('click', this._onPlayBtnClick.bind(this));
//...
return this;
},
_onPlayBtnClick: function (e) {
e.preventDefault(); //prevents the browser's default behaviour
this.play();
},
//...
};
Then you could do something like:
function load() {
var musicController = Object.create(Music).init({
playBtn: '#play',
//...
});
}
You could also use it as a singleton like:
Music.init({ ... });

Youtube api does not load on firefox

I have the following code snippet that controls an embedded youtube player. It works great on Chrome and Safari but not on Firefox.
jsfiddle : http://jsfiddle.net/fuSSn/4/
Code from my app:
the iframe:
<div class="tubeframe" id="video-frame-155" style="">
<iframe title="YouTube video player" width="350" height="262" src="http://www.youtube.com/embed/hc5xkf9JqoE?HD=1;rel=0;showinfo=0;autohide=1" frameborder="0" allowfullscreen="" id="video-frame-155-frame"></iframe>
</div>
my javascript:
var source_tag = document.createElement("script");
var first_source_tag = document.getElementsByTagName("script")[0];
first_source_tag.parentNode.insertBefore(source_tag, first_source_tag);
// This function will be called when the API is fully loaded
function onYouTubeIframeAPIReady() {
YT_ready(true)
console.log("api loaded! yikes")
}
function getFrameID(id){
var elem = document.getElementById(id);
if (elem) {
if(/^iframe$/i.test(elem.tagName)) return id; //Frame, OK
// else: Look for frame
var elems = elem.getElementsByTagName("iframe");
if (!elems.length) return null; //No iframe found, FAILURE
for (var i=0; i<elems.length; i++) {
if (/^https?:\/\/(?:www\.)?youtube(?:-nocookie)?\.com(\/|$)/i.test(elems[i].src)) break;
}
elem = elems[i]; //The only, or the best iFrame
if (elem.id) return elem.id; //Existing ID, return it
// else: Create a new ID
do { //Keep postfixing `-frame` until the ID is unique
id += "-frame";
} while (document.getElementById(id));
elem.id = id;
return id;
}
// If no element, return null.
return null;
}
// Define YT_ready function.
var YT_ready = (function(){
var onReady_funcs = [], api_isReady = false;
return function(func, b_before){
if (func === true) {
api_isReady = true;
while(onReady_funcs.length > 0){
// Removes the first func from the array, and execute func
onReady_funcs.shift()();
}
}
else if(typeof func == "function") {
if (api_isReady) func();
else onReady_funcs[b_before?"unshift":"push"](func);
}
}
})();
var video = function ( videoid, frameid) {
var player;
var that;
var seconds;
var duration;
var stateChangeCallback;
var update_play = 0;
return {
setOnStateChangeCallback: function(callback) {
stateChangeCallback = callback;
},
getCurrentTime: function() {
return player.getCurrentTime();
},
getPlayer: function () {
return player;
},
getVideoFrameId: function () {
return "video-frame-" + videoid;
},
initVideo: function (second) {
console.log("initing")
that = this;
YT_ready(function(){
var frameID = getFrameID("video-frame-" + videoid);
console.log("creating player")
console.log(frameID)
if (frameID) { //If the frame exists
console.log("frame exists")
player = new YT.Player(frameID, {
events: {
"onStateChange": that.stateChange
}
});
console.log("Player Created!");
if (second) {
console.log(second)
setTimeout(function() { console.log("seek to"); player.seekTo(second, false); player.stopVideo()}, 1000);
}
}
});
},
stateChange: function (event) {
console.log("event.data = ", event.data);
switch(event.data) {
case YT.PlayerState.PLAYING:
{
if (stateChangeCallback)
stateChangeCallback("play", player.getCurrentTime(), player.getDuration());
onsole.log("play");
}
break;
case YT.PlayerState.PAUSED:
case YT.PlayerState.CUED:
case YT.PlayerState.ENDED:
{
if (stateChangeCallback)
stateChangeCallback("pause", player.getCurrentTime(), player.getDuration());
console.log("pause");
}
break;
}
},
pauseVideo: function () {
player.stopVideo();
console.log('player.stopVid()');
},
seekTo: function(second) {
player.seekTo(second, false);
}
};
};
function onStateChange(vid, action, second, total) {
if (Videos[vid]) {
console.log( (second / total) * 100);
}
};
$(document).ready(function () {
var Videos = {};
logger.info("heyyy")
var videoId=155;
//if (videoId) {
Videos[videoId] = video(videoId, 155);
console.log(Videos[155])
Videos[155].initVideo();
Videos[155].setOnStateChangeCallback(function(action, second, total) {
onStateChange(155, action, second, total);
});
//}
Videos[155].seekTo(1000, false);
onStateChange(155, "start", 0, 0);
});
I know that the required script tags are being added, I can test that from console. I also know that onYouTubePlayerAPIReady() is actually called. But I still receive errors like
TypeError: player.stopVideo is not a function
When I run the three lines that adds the source tag again from the web console on firefox, the api seems to load and everything starts working again.
I have been struggling with this for days and I really need help figuring out what might be wrong. If it helps my application is developed in ruby on rails but I don't think this is relevant information.
Thanks
There is no problem with the above code. My video was loaded in a bootstrap modal. Modal's hide property would make it invisible to firefox and firefox would not load the api at all. So I removed the modal hide class and instead of display:none I used item.css("visibility", "visible"); and item.css("visibility", "hidden"); which made firefox load the api.

Categories