I am trying to make a html5 player with a playlist so that when a song ends the next one begins automatically.
Im using jQuery.
This is my Javascript:
$(function(){
var player = $('#player')
var index = 0
var CurrentTime = 0
var tracks = [ {'source' : 'audio1.mp3'},
{'source' : 'audio2.mp3'},
{'source' : 'audio3.mp3'}]
var CurrentTrack = tracks[index]
function updateCookie(){
Cookies.set('trackIndex', index)
Cookies.set('time', CurrentTime)
}
//Check if the user has been here before
if(!!Cookies.get('trackIndex')){
updateCookie()
}
else {
index = Cookies.get('trackIndex')
CurrentTime = Cookies.get('time')
}
function playerSetUp(){
player[0].currentSrc = CurrentTrack.source
player[0].currentTime = CurrentTime
}
player.bind('ended',function(){
index++
player[0].currentSrc = CurrentTrack.source
console.log($('#player').currentSrc)
player[0].load()
player[0].play()
})
player.bind('pause',function(){
CurrentTime = $('#player').currentTime
updateCookie()
})
})
And this is my html:
<html>
<head>
<title>Mixtape</title>
<script type="text/javascript" src="https://code.jquery.com/jquery-
3.2.1.min.js"></script>
<script src="js.cookie.js"></script>
<script type="text/javascript" src="player.js"></script>
<head>
<body>
<audio controls autoplay id='player'>
<source src="audio1.mp3">
Fallback
</audio>
</body>
I want to create a player that imitates the behavior of an analog audio tape. I want it to play one song after the other and when you close the page and open it again the player will continue from the last track and time you paused it.
The problem is my code above doesn't respond to the events.
What am I doing wrong?
You have three errors in your code, the event listeners are working.
Use:
//Check if the user has been here before
if(!Cookies.get('trackIndex')){
updateCookie()
CurrentTrack = tracks[index]
}
else {
index = Cookies.get('trackIndex')
CurrentTime = Cookies.get('time')
CurrentTrack = tracks[index]
}
(Just one "!"). And you need to update "CurrentTrack" each time you modify your "index", because you just set currentTrack one time.
And:
playerSetUp();
player.bind('ended', function() {
index++;
index = index % tracks.length
CurrentTrack = tracks[index]
player[0].currentSrc = CurrentTrack.source
console.log(player[0].currentSrc)
player[0].load()
player[0].play()
})
player.bind('pause', function() {
CurrentTime = player[0].currentTime
updateCookie()
})
You need to call playerSetUp() and index = index % tracks.length so that your index wont be bigger than your tracks array.
One more thing: player[0].currentSrc is readonly. You need to use player[0].src.
Related
I have function running every second (for clock) .
I wanted to add sound to every tick tick....(second) but as function is running automatic (means no triggering is taking place) and when sound added produces an error
Error showing is :
Uncaught DOMException: play() failed because the user didn't interact with the document first.
Know that it is not possible to play audio automatic .
So tried to create a start sound btn which play the audio but as second time (of clock) and don't match with audio . So there is a mismatch
I am trying to add that function(audio play) inside second(clock time) calculator .
Is this possible or not
Link to similar question but not able to find the solution
The code is as follows
setInterval(clockRunner, 1000);
function clockRunner() {
//Clock code goes here
function trigger() {
var audioElement = document.getElementById('audiotag1');
audioElement.play();
}
}
<audio id="audiotag1" src="https://soundbible.com/mp3/Tick-DeepFrozenApps-397275646.mp3" preload="metadata" controls></audio>
<button onclick="trigger()">Start</button>
Update
Added event listener inside the outer function but this way sound is playing once not every second .
One solution here can be adding another setInterval but this will again create problem of playing sound at different timing of clock's second
Also it shows the same error in the console(mentioned above) until user clicks the btn
setInterval(clockRunner, 1000);
function clockRunner() {
//Clock code goes here
document.getElementById('demo').innerHTML = new Date();
setInterval(trigger, 1000);
document.getElementById("triggerSnd").addEventListener('click', trigger)
function trigger() {
var audioElement = document.getElementById('audiotag1');
audioElement.play();
}
}
<audio id="audiotag1" src="https://soundbible.com/mp3/Tick-DeepFrozenApps-397275646.mp3" preload="metadata" controls></audio>
<button id="triggerSnd">Start</button>
<div id="demo"></div>
Thanks for help in advance
If you make use of some variables, you can control your code to perform like a clock. It's worth noting that there are two possibilities, either the clock has started or it has stopped.
The interval will run when you clicked to start and it will be cleared in stopping it. Here I can't notice any mismatch between the sound and the timer.
var start = document.getElementById("triggerSnd")
var audioElement = document.getElementById('audiotag1')
var demo = document.getElementById('demo')
var interval, isMuted = true
// The second argument of addEventListener should be a function
// that receives an argument 'event'
clockRunner();
clock(); // call firstly
setInterval(() => clock(), 1000)
start.addEventListener('click', clockRunner)
function clock(isMuted){
//Clock code goes here
demo.innerText = new Date();
audioElement.play();
}
function clockRunner() {
audioElement.muted = isMuted
start.innerText = isMuted ? "Start" : "Stop"
isMuted = !isMuted
}
<audio id="audiotag1" src="https://soundbible.com/mp3/Tick-DeepFrozenApps-397275646.mp3" preload="metadata"></audio>
<button id="triggerSnd">Start</button>
<div id="demo"></div>
I've changed my answer completely. Now I didn't need to use an event listener, just the proper API of HTMLMediaElement that audio uses. Note that I've add loop attribute to tick's audio.
var blast = document.getElementById("blast");
var tick = document.getElementById("tick");
var tickDuration, timeDiff, lastTime
tick.onloadedmetadata = () => tickDuration = tick.duration
timeDiff = 0.3 // max difference between sync, dont lower it or it will bug (can explain if u want)
lastTime = 0
blast.addEventListener("timeupdate", () => {
let bct = blast.currentTime
let tct = tick.currentTime
let syncBlastTime = tickDuration - (tickDuration - bct%tickDuration)
let diff = Math.abs(syncBlastTime - tct)
lastTime = bct
console.log(`Blast: ${bct} Tick: ${tct}\nSynced time difference (seconds): ${diff.toFixed(6)}`)
if(diff > timeDiff) { // add this or it will bug the tick audio
tick.currentTime = syncBlastTime
}
})
blast.addEventListener("pause", () => {
tick.pause()
})
blast.addEventListener("playing", () => {
tick.play()
})
<audio id="blast" src="http://stream.arrowcaz.nl/caz64kmp3" controls="loop"></audio>
<audio id="tick" src="https://soundbible.com/mp3/A-Z_Vocalized-Mike_Koenig-197996831.mp3" loop controls='loop'></audio>
<br />
I have 5 div boxes that play a certain track on click (using data attributes), however, I can't seem to figure out how to pause the previous track on next click. The audio plays on top of each other.
Javascript:
var audio = ['song-1.mp3','song-2.mp3','song-3.mp3','song-4.mp3','song-
5.mp3'];
var music = document.querySelector('#container');
music.addEventListener('click', function(evt){
if(evt.target.tagName === "DIV"){
var index = evt.target.getAttribute('data-index');
var sound = new Audio(audio[index]);
sound.play();
audio.forEach(function(x){
if(x !== sound){
sound.pause();
}
})
}
});
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<section id="container">
<div data-index="0"><p>40's</p></div>
<div data-index="1"><p>50's</p></div>
<div data-index="2"><p>60's</p></div>
<div data-index="3"><p>70's</p></div>
<div data-index="4"><p>80's</p></div>
<div data-index="5"><p>90's</p></div>
<div data-index="6"><p>2000's</p></div>
<div data-index="7"><p>2010's</p></div>
</section>
<script src='script.js'></script>
</body>
</html>
sound will always refers to the most recently clicked sound, never to the one that was playing before. The easiest solution is probably to keep a variable playing that tracks the audio object that is currently playing. That way when a new sound is played, you have a reference to the old one that you can pause. That could look something like this
var audio = ['song-1.mp3','song-2.mp3','song-3.mp3','song-4.mp3','song-
5.mp3'];
var music = document.querySelector('#container');
var playing = undefined;
music.addEventListener('click', function(evt){
if(evt.target.tagName === "DIV"){
var index = evt.target.getAttribute('data-index');
var sound = new Audio(audio[index]);
if (playing) playing.pause();
sound.play();
playing = sound;
}
});
Another option is for you to just keep an array of audio objects and track the index of the currently playing audio. That could look like this. This may be preferable because it will keep the place of paused songs.
var audioNames = ['song-1.mp3','song-2.mp3','song-3.mp3','song-4.mp3','song-
5.mp3'];
var audio = [];
for (var a in audioNames) {
audio.push(new Audio(a));
}
var music = document.querySelector('#container');
var playingIndex = undefined;
music.addEventListener('click', function(evt){
if(evt.target.tagName === "DIV"){
var index = evt.target.getAttribute('data-index');
var sound = audio[index];
if (playing) audio[playingIndex].pause();
sound.play();
playingIndex = index;
}
});
I was trying to play a list of audio files on the same web page with only a text link as play button, and I found the following solution, which works really perfect for me:
(thanks #linstantnoodles for this script!)
<audio id="song1" preload='auto'>
<source src='song1.mp3' type='audio/mp3' />
</audio>
<audio id="song2" preload='auto'>
<source src='song2.mp3' type='audio/mp3' />
</audio>
Listen
Listen
<script type="text/javascript">
// List of audio/control pairs
var playlist = [{
"audio": document.getElementById('song1'),
"control": document.getElementById('song1-control')
}, {
"audio": document.getElementById('song2'),
"control": document.getElementById('song2-control')
}];
for (var i = 0; i < playlist.length; i++) {
var obj = playlist[i];
var audio = obj.audio;
var control = obj.control;
// returns a closure
var listener = function (control, audio) {
return function () {
var pause = control.innerHTML === 'Pause';
control.innerHTML = pause ? 'Listen' : 'Pause';
// Update the Audio
var method = pause ? 'pause' : 'play';
audio[method]();
// Prevent Default Action
return false;
}
};
control.addEventListener("click", listener(control, audio));
}
</script>
My only problem with this code is, that if one song is playing, and I start a second one, both are playing at the same time. I've tried many ways to get this problem fixed, but none of them did work - how do I need to adapt it, so that the first one pauses or stops when a second one starts? Thanks!
You can do something like this. When a new audio is started, loop through the playlist array to find all the audio elements that are not the one that just got started and stop them.
Here is an example:
....
var method = pause ? 'pause' : 'play';
audio[method]();
if(!pause){
playlist.forEach(function(pl){
if(pl.audio !== audio){
pl.audio.pause();
pl.audio.currentTime = 0;
pl.control.innerHTML = 'Listen';
}
});
}
....
If I have HTML5 video and audio elements, is there a clean way to keep them in sync? They should act like a video file that contains an audio track, so advancing one track manually should bring the other one along with it. Let's say the two tracks have the same duration.
I'd like a solution that works across browsers, but I don't particularly care if it works on older browsers. It would also be nice to avoid the use of JavaScript if possible. Otherwise, a dead-simple JavaScript library would be best -- something that only asks for which tracks to synchronize and takes care of the rest.
I've looked into mediagroup... but it looks like it only works in Safari. I've looked into audioTracks... but the user has to enable the feature in Firefox.
I've looked into Popcorn.js, which is a JavaScript framework that seems designed for this task... but it looks like there hasn't been any activity in over a year. Besides that, the only demonstrations I can find are of synchronizing things like text or slides to video, not audio to video.
You can use Promise.all(), fetch() to retrieve media resource as a Blob, URL.createObjectURL() to create a Blob URL of resource; canplaythrough event and Array.prototype.every() to check if each resource can play; call .play() on each resource when both resources can play
I didn't make it clear in the question. I meant that the two tracks
should stay in sync as though playing a regular video file with audio.
One approach could be to create a timestamp variable, utilize seeked event to update .currentTime of elements when set variable is greater than a minimum delay to prevent .currentTime being called recursively.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button>load media</button>
<br>
<div id="loading" style="display:none;">loading media...</div>
<script>
var mediaBlobs = [];
var mediaTypes = [];
var mediaLoaded = [];
var button = document.querySelector("button");
var loading = document.getElementById("loading");
var curr = void 0;
var loadMedia = () => {
loading.style.display = "block";
button.setAttribute("disabled", "disabled");
return Promise.all([
// `RETURN` by smiling cynic are licensed under a Creative Commons Attribution 3.0 Unported License
fetch("https://ia600305.us.archive.org/30/items/return_201605/return.mp3")
, fetch("http://nickdesaulniers.github.io/netfix/demo/frag_bunny.mp4")
])
.then(responses => responses.map(media => ({
[media.headers.get("Content-Type").split("/")[0]]: media.blob()
})))
.then(data => {
for (var currentMedia = 0; currentMedia < data.length; currentMedia++) {
(function(i) {
var type = Object.keys(data[i])[0];
mediaTypes.push(type);
console.log(data[i]);
var mediaElement = document.createElement(type);
mediaElement.id = type;
var label = document.createElement("label");
mediaElement.setAttribute("controls", "controls");
mediaElement.oncanplaythrough = () => {
mediaLoaded.push(true);
if (mediaLoaded.length === data.length
&& mediaLoaded.every(Boolean)) {
loading.style.display = "none";
for (var track = 0; track < mediaTypes.length; track++) {
document.getElementById(mediaTypes[track]).play();
console.log(document.getElementById(mediaTypes[track]));
}
}
}
var seek = (e) => {
if (!curr || new Date().getTime() - curr > 20) {
document.getElementById(
mediaTypes.filter(id => id !== e.target.id)[0]
).currentTime = e.target.currentTime;
curr = new Date().getTime();
}
}
mediaElement.onseeked = seek;
mediaElement.onended = () => {
for (var track = 0; track < mediaTypes.length; track++) {
document.getElementById(mediaTypes[track]).pause()
}
}
mediaElement.ontimeupdate = (e) => {
e.target.previousElementSibling
.innerHTML = `${mediaTypes[i]} currentTime: ${Math.round(e.target.currentTime)}<br>`;
}
data[i][type].then(blob => {
mediaBlobs.push(URL.createObjectURL(blob));
mediaElement.src = mediaBlobs[mediaBlobs.length - 1];
document.body.appendChild(mediaElement);
document.body.insertBefore(label, mediaElement);
})
}(currentMedia));
}
})
};
button.addEventListener("click", loadMedia);
</script>
</body>
</html>
plnkr http://plnkr.co/edit/r51oh7KsZv8DEYhpRJlL?p=preview
To my knowledge, this is impossible with pure HTML.
You can use the currentTime property of both <video> and <audio> elements to synchronize the time.
var video = document.getElementById("your-video");
var audio = document.getElementByid("your-audio");
audio.currentTime = video.currentTime;
If necessary, you could also use the timeupdate event to continuously re-sync the video and audio.
I have urls of several audio files like this:
example.net/{num}.mp3
And I would like to play them one after the other without a delay between them.
I tried having two audio elements with preload=true and switching between them, but I still have a delay.
Any ideas ?
I'd like to see the actual code of what you have so far, because I'm not 100% sure what "switching between them" means, but I wrote the following example which starts the next file playing before the first one finishes. You specify a set amount of "overlap", and it starts playing the next file JSFiddle: http://jsfiddle.net/76xqhupw/4/
const OVERLAP_TIME = 1.0; //seconds
const song1 = document.getElementById('audio1');
const song2 = document.getElementById('audio2');
song1.addEventListener("timeupdate", function() {
if (song1.duration - song1.currentTime <= OVERLAP_TIME) {
song2.play();
}
});
song1.play();
This is just relevant to playing the next audio element instantly, since you said you already had two audio elements, but if I'm missing something let me know.
<html>
<head>
<title>Subway Maps</title>
<link rel="stylesheet" type="text/css" href="main.css" />
</head>
<body onload="onload();">
<video id="idle_video" width="1280" height="720" onended="onVideoEnded();"></video>
<script>
var video_list = ["Skydiving Video - Hotel Room Reservation while in FreeFall - Hotels.com.mp4",
"Experience Conrad Hotels & Resorts.mp4",
"Mount Airy Casino Resort- Summer TV Ad 2.mp4"
];
var video_index = 0;
var video_player = null;
function onload(){
console.log("body loaded");
video_player = document.getElementById("idle_video");
video_player.setAttribute("src", video_list[video_index]);
video_player.play();
}
function onVideoEnded(){
console.log("video ended");
if(video_index < video_list.length - 1){
video_index++;
}
else{
video_index = 0;
}
video_player.setAttribute("src", video_list[video_index]);
video_player.play();
}
</script>
</body>
for refrence how to display multiple videos one by one dynamically in loop using HTML5 and javascript
You can use ended event, Array.prototype.shift() to set src of element to next item within array, if array .length is greater than 0, call .load(), .play()
var url = "example.net/"
var type = ".mp3";
var n = [0,1,2,3,4,5];
var audio = document.querySelector("audio");
audio.onended = function() {
if (n.length) {
this.src = url + n.shift() + type;
this.load();
this.play();
}
}
audio.src = url + n[0] + type;
audio.load();
audio.play();