I've been playing with HTML5 audio recently, and though I can get it to play the sound it only ever will play once. No matter what I try (setting the properties, event handlers, etc) I can't seem to get it to loop.
Here's the basic code I'm using:
//myAudio is declared at a global scope, so it doesn't get garbage collected.
myAudio = new Audio('someSound.ogg');
myAudio.loop = true;
myAudio.play();
I'm testing using Chrome (6.0.466.0 dev) and Firefox (4 beta 1), both of which seem happy to ignore my requests for looping. Any ideas?
UPDATE: The loop property is now supported in all major browsers.
While loop is specified, it is not implemented in any browser I am aware of Firefox [thanks Anurag for pointing this out]. Here is an alternate way of looping that should work in HTML5 capable browsers:
var myAudio = new Audio('someSound.ogg');
myAudio.addEventListener('ended', function() {
this.currentTime = 0;
this.play();
}, false);
myAudio.play();
To add some more advice combining the suggestions of #kingjeffrey and #CMS: You can use loop where it is available and fall back on kingjeffrey's event handler when it isn't. There's a good reason why you want to use loop and not write your own event handler: As discussed in the Mozilla bug report, while loop currently doesn't loop seamlessly (without a gap) in any browser I know of, it's certainly possible and likely to become standard in the future. Your own event handler will never be seamless in any browser (since it has to pump around through the JavaScript event loop). Therefore, it's best to use loop where possible instead of writing your own event. As CMS pointed out in a comment on Anurag's answer, you can detect support for loop by querying the loop variable -- if it is supported it will be a boolean (false), otherwise it will be undefined, as it currently is in Firefox.
Putting these together:
myAudio = new Audio('someSound.ogg');
if (typeof myAudio.loop == 'boolean')
{
myAudio.loop = true;
}
else
{
myAudio.addEventListener('ended', function() {
this.currentTime = 0;
this.play();
}, false);
}
myAudio.play();
Your code works for me on Chrome (5.0.375), and Safari (5.0). Doesn't loop on Firefox (3.6).
See example.
var song = new Audio("file");
song.loop = true;
document.body.appendChild(song);
Simplest way is:
bgSound = new Audio("sounds/background.mp3");
bgSound.loop = true;
bgSound.play();
var audio = new Audio("http://rho.nu/pub/Game%20Of%20Thrones%20-%20Main%20Theme%20-%20Soundtrack.mp3");
audio.addEventListener('canplaythrough', function() {
this.currentTime = this.duration - 10;
this.loop = true;
this.play();
});
Just set loop = true in the canplaythrough eventlistener.
http://jsbin.com/ciforiwano/1/edit?html,css,js,output
Try using jQuery for the event listener, it will then work in Firefox.
myAudio = new Audio('someSound.ogg');
$(myAudio).bind('ended', function() {
myAudio.currentTime = 0;
myAudio.play();
});
myAudio.play();
Something like that.
I did it this way,
<audio controls="controls" loop="loop">
<source src="someSound.ogg" type="audio/ogg" />
</audio>
and it looks like this
This works and it is a lot easier to toggle that the methods above:
use inline: onended="if($(this).attr('data-loop')){ this.currentTime = 0; this.play(); }"
Turn the looping on by $(audio_element).attr('data-loop','1');
Turn the looping off by $(audio_element).removeAttr('data-loop');
You could try a setInterval, if you know the exact length of the sound. You could have the setInterval play the sound every x seconds. X would be the length of your sound.
Everyone knows now if you type:
<audio controls="controls" loop="loop">
<source src="someSound.ogg" type="audio/ogg" />
</audio>
It will play the song and it will be looping
But there is a shorter way to play the song continuously:
<audio controls loop src="someSound.ogg"></audio>
Related
So I'm having a little trouble with videos, i have a website and there i have the same video displayed in 3 different pages.
The videos are all paused until the user clicks on them to start.
The problem is when, i leave a page and the video is just there even if i have clicked on play or pause or haven't done anything at all, the other two, give me an error saying vid.pause is not a function.
This is the HTML part ->
<video id="{{vid?.id}}" src={{vid?.video}} onloadedmetadata="this.paused = true; this.currentTime = 1" (click)="playPause(vid)"> </video>
And the js ->
playPause(vid) {
var vid = (<HTMLMediaElement>document.getElementById(vid.id));
if (vid.paused == true) {
vid.play();
} else {
vid.pause();
vid.currentTime = 1;
}
}
The problem for me was that I tried pausing a jQuery object, but that's not possible. You need to pause a DOM element. So I corrected it by converting my jQuery object to a DOM element.
BEFORE:
$("#video").pause();
AFTER / SOLVED:
$("#video)[0].pause();
UPDATE:
One thing to notice is:
You are passing the vid property to the element and then back to the .ts. This is not good practices.
Also, you are using variables inside properties the wrong way.
The best way to do this would be:
.html file
<video #myVideo [id]="vid?.id" [src]="vid?.video" onloadedmetadata="this.paused = true; this.currentTime = 1" (click)="playPause(myVideo)"> </video>
.ts file
playPause(vid: HTMLMediaElement) {
if (vid.paused == true) {
vid.play();
} else {
vid.pause();
vid.currentTime = 1;
}
}
My guess is you have a difference in your HTML and JS files.
In your HTML file, you have the video id as vid.id.
In your JS file, you are using getElementById, passing the id as vid.video.
Therefore var vid will be undefined and vid.pause() won't be a function.
You probably have to use var vid = (<HTMLMediaElement>document.getElementById(vid.id));
I am building a web-app which includes a form and (as a progressive enhancement / UX effect) I would like a keypress to fire a keystroke sound effect.
I have cut the .mp3 sound effect quite short (0.18s).
Nevertheless, there appears to be an audible delay when I test my setup on Firefox on my laptop (and not every keypress fires the sound effect).
On Firefox Mobile on my Android, very few keypresses fire the sound effect - maybe one keypress in every eight.
Am I trying to achieve something which can't be done using present web technology, or am I simply approaching this the wrong way?
Here is my setup:
var myInput = document.getElementsByTagName('input')[0];
var keypress = document.getElementsByClassName('keypress')[0];
function playSoundEffect() {
keypress.play();
}
myInput.addEventListener('keydown', playSoundEffect, false);
/* I've also tried...
myInput.addEventListener('keyup', playSoundEffect, false);
myInput.addEventListener('keypress', playSoundEffect, false);
*/
input {
width: 90vw;
}
<input type="text" placeholder="Type to hear the sound effect..." />
<audio class="keypress">
<source src="http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3" type="audio/mpeg">
</audio>
Second Attempt:
Thanks to the helpful comments below from #StackingForHeap and #zero298, I have refactored my setup:
var myInput = document.getElementsByTagName('input')[0];
function playSoundEffect() {
var jsKeypress = new Audio('http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3');
jsKeypress.play();
}
myInput.addEventListener('input', playSoundEffect, false);
input {
width: 90vw;
}
<input type="text" placeholder="Type to hear the sound effect..." />
This is definitely an improvement - creating a new Audio object each time allows for each one to start playing independently of any previously created Audio objects.
I have taken a look at your second attempt and it seems sound (excuse the pun). However, I just took a crack at it and I am able to get pretty good results with this:
This uses an audio file that I found on Freesound.org: UI Confirmation Alert, C4.wav.
This creates an AudioContext instead of just an Audio and uses the same, already loaded, buffer over and over again. I'm still trying to see if there is a performance gain there.
Looks like you have to make source nodes over and over again:
An AudioBufferSourceNode can only be played once; after each call to
start(), you have to create a new node if you want to play the same
sound again. Fortunately, these nodes are very inexpensive to create,
and the actual AudioBuffers can be reused for multiple plays of the
sound. Indeed, you can use these nodes in a "fire and forget" manner:
create the node, call start() to begin playing the sound, and don't
even bother to hold a reference to it. It will automatically be
garbage-collected at an appropriate time, which won't be until
sometime after the sound has finished playing.
/*jslint browser:true, esversion:6, devel:true*/
/*global AudioContext*/
(function() {
"use strict";
function main() {
let ctx = new AudioContext();
fetch("/res/audio/twinkle.wav")
.then(res => res.arrayBuffer())
.then((buffer) => {
return new Promise((resolve, reject) => {
ctx.decodeAudioData(buffer, (audioBuffer) => {
resolve(audioBuffer);
});
});
})
.then((audioBuffer) => {
document.addEventListener("keydown", (e) => {
console.log(e.keyCode);
if (e.keyCode === 37) { // The "left arrow" key
let source = ctx.createBufferSource();
source.buffer = audioBuffer;
source.connect(ctx.destination);
source.start(0);
}
});
});
}
document.addEventListener("DOMContentLoaded", main);
}());
I try doing the same process with the howler.js library, and it seems to work very well.
this is the example
var myInput = document.getElementsByTagName('input')[0];
var sound = new Howl({
src:['http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3']
});
sound.play();
function playSoundEffect() {
sound.play();
}
myInput.addEventListener('keydown', playSoundEffect, false);
I partially achieved playing sound with keys with html and .play. However it waits for the track to be completed and doesn't let me play instantly on every click. However I want to trigger the sound as soon as I press it each time.
<audio id=soundId1>
<source src=sound/Saaa.wav>
</audio>
<audio id=soundId2>
<source src=sound/Baaa.wav>
</audio>
$(document).ready(function() {
var sound = {
65 : 'soundId1',
23 : 'soundId2'
};
document.onkeydown = function(e) {
var soundId = sound[e.keyCode];
if (soundId) document.getElementById(soundId).play();
else console.log("key not mapped : code is", e.keyCode);
}
Is there a way of achieving it with the above approach only without using any library?
I also looked into howler.js (I don't know if it's proper use) but I couldn't really understand how I can add multiple tracks into the dictionary. I understand how I can put the files into sounds dictionary however how to call it and link it to keyCode system above? Or should I add a new Howl for each sound individually?
What is the proper way of achieving it?
This may work but I have not tried it:
function Sound(){
this.src = ""
}
function playSound(source){
var sound = new Sound();
sound.src = source;
sound.play()
}
A new sound instance is created instead of overriding the previous one.
I'm trying to preload audio files for a game. I'm using Jplayer. I have an overlay that gets removed once all audio has been preloaded. In Firefox, sometimes canplaythrough gets fired, sometimes not. I have five audio files, sometimes I get three canplaythrough events, sometimes four, not often do I get all of them. If I log which ones work, the event isn't always fired, or not, on the same audio files.
I've tried to break the code as much as possible. Here I am adding a new Jplayer instance for each audio file, I still get the same problem.
{
for(var i = 0; i < _timeOutAudioFilesToLoad; i++){
c = i + 1;
var elid = "timeOutAudio"+c;
var elt = '<div id="'+elid+'" class="audioPlayer audio-player" data-audio-file="/themes/foo/sounds/time'+c+'.mp3"></div>';
$("#jPlayers").append(elt);
$("#"+elid).jPlayer( {
swfPath: "/themes/foo/js/libs/jPlayer250/Jplayer.swf"
});
}
}
{
$(".audioPlayer").each(function(){
var audioFile = $(this).attr("data-audio-file");
$(this).bind($.jPlayer.event.canplaythrough, function(event) {
_loadWatcher();
});
$(this).jPlayer("setMedia", {mp3: audioFile});
})
}
The _loadWatcher() function gets called by some, but not others.
Am I doing something wrong? Or is this a bug with a workaround? I've tried the Jplayer Google Group, but for some reason, they're taking days and days to mod questions.
Thanks
My problem was that I was binding the event before instantiating jPlayer on the element. I didn't realise that jplayer was actually waiting for a custom event - not the standard HTML5 event - so I thought binding anywhere would be OK. You live and learn. So switching the order of the last two lines in the for loop fixed the problem.
I'm trying to control html5 video with javascript. What I want is that when the user clicks on a button, the video will jump to another frame and keep playing from there. With my current code the playback always stops after the seek.
function jumpToTime(){
var video = $("video")[0];
video.currentTime = 160;
document.getElementbyId("videoclip").play(); //not working
};
//the following code is probably not really related to the question:
var endtimeoffset = 2000;
video.addEventListener("timeupdate", function() {
if (video.currentTime >= endtimeoffset) {
video.pause();
}
}, false);
I ran into a similar problem, and found a solution by pausing the video, then setting the currentTime, then playing the video. To update your code:
function jumpToTime(){
var video = $("video")[0];
video.pause();
video.currentTime = 160;
video.play();
};
Some things I would try:
in the jumpToTime() function, you have two different references to supposedly the same video (one obtained through jQuery and the other by getElementById()). Are you sure these reference the same video? To be safe, I would just call play() on the 'video' reference that you set the currentTime on.
This is probably a copy and paste issue since the console would complain if this was in the actual code, but you did mispell getElementById() (Need to capitalize the B).
For debugging purposes, I would comment out the 'timeupdate' event code, to make sure this isn't the issue and that this code isn't pausing the video after you update the timehead or call play. It probably isn't, since you are setting the current time to be much less than the offset you are comparing it with. It would, however, be an easy test to eliminate this as a possible cause of the issue.
function jumpToTime(){
document.getElementById("videoclip").currentTime = 160;
document.getElementById("videoclip").play(); //not working
};
getElementbyId --> getElementById -- b --> B
get direct object by id...