How can I determine if standard audio player is playing a song - html5-audio

I have the following as part of my html (once for each of several songs):
<audio
name="aMedia"
id="aMedia"
controls
preload="auto">
<source src="audio/mysong.m4a">
</audio>
I wish to determine if the above standard Play button was pressed to play a song. If it was, I need to first stop all other songs that may be playing, e.g.,
function stopAllSongs()
{
var allMedia = document.getElementsByName(theMedia), thisMedia;
for (m=0; m < allMedia.length; m++)
{
thisMedia = allMedia[m];
if (thisMedia.isPlaying())
thisMedia.pause();
}
}
I have seen successful uses of <input ...> to do what I wish; but I would like to avoid that if possible.
Any ideas?

You could use the play event for this, then iterate through all other audio elements and invoke pause on them.
You can use a common handler for all the audio elements, the this object will represent the actual audio element triggering the event (which in turn of course can be used to set a CSS class etc.).
pause() can be called even if the audio is already paused so we don't need to do additional checks for it. We just loop through the array of elements and skip the element that triggered the event.
Example
var audioElements = document.querySelectorAll("audio"), i;
// bind common handler for each audio element in page:
for(i = 0; i < audioElements.length; i++)
audioElements[i].addEventListener("play", playHandler);
// common handler
function playHandler() {
// this = element triggering the event
// pause all but ours (reusing previous audioElements NodeList collection here)
for(var i = 0; i < audioElements.length; i++)
if (this !== audioElements[i]) audioElements[i].pause();
}
<i>Preload time may vary, be patient... (due to links IE will not be able to play these)</i><br>Starting one will stop all others<br>
<audio preload="auto" controls src="https://github.com/epistemex/free-music-for-test-and-demo/blob/master/music/kf_feelme.mp3?raw=true"></audio><br>
<audio preload="auto" controls src="https://github.com/epistemex/free-music-for-test-and-demo/blob/master/music/kf_colibris.mp3?raw=true"></audio><br>
<audio preload="auto" controls src="https://github.com/epistemex/free-music-for-test-and-demo/blob/master/music/kf_limitless.mp3?raw=true"></audio><br>
<audio preload="auto" controls src="https://github.com/epistemex/free-music-for-test-and-demo/blob/master/music/kf_thebattle.mp3?raw=true"></audio>

Related

HTML5 audio playlist start differently on page load

I have a basic audio playlist which plays continuously several audio tracks in specic order. How can I change the starting track on page load, so that everytime the page is loaded the playlist starts at different track? I just found solutions with shuffle mode, but this changes the playlist order, which should keep the same. Just looking for a random start when page is loaded. Thanks for any suggestion!
Here is my working code for the continuously playing playlist.
const shuffledPaths = [
'https://estherhunziker.net/_sinai/test_01.mp3',
'https://estherhunziker.net/_sinai/test_02.mp3',
'https://estherhunziker.net/_sinai/test_03.mp3',
'https://estherhunziker.net/_sinai/test_04.mp3',
'https://estherhunziker.net/_sinai/test_05.mp3'
];
// You should probably use more specific selectors, this is for the sample
const player = document.querySelector('audio');
const source = document.querySelector('source');
let currentIndex = -1;
function goNext() {
// Grab the next path and put it in the source's src attribute
currentIndex = (currentIndex + 1) % shuffledPaths.length;
source.setAttribute('src', shuffledPaths[currentIndex]);
// Load the corresponding track and play it
player.load();
player.play();
}
// Play the following track when the current one ended
player.addEventListener('ended', goNext);
// Play the first track
goNext();
<audio controls="controls" >
<source type="audio/mpeg">
</audio>
Instead of initializing currentIndex as -1, you could initialize it as a random index:
// take a random index in the array, then subtract 1, because the goNext function adds 1 before playing
let currentIndex = Math.floor(Math.random() * shuffledPaths.length) - 1;

How to detect when HTML5 video is played the first frame?

I ask this because I would like to show a overlay image on exact play time like on 3:00.
Therefore, I binded the "play" event and state setTimeout(...,18000) to show the image.
However, it seems the setTimeout is done too early. The image is shown faster than expected. I can unterstand as the "play" event is fired BEFORE the video is prepared.
So I also tried to bind "loadeddata" and "playing" event. But this time, the image is shown slower than expected.
Also, the image seems to show in "random manner" (not always show on the same time with a few tests, a little bit different).
Is there a event that I can use setTimeout to show image on exact time? Or in simple, how can I show an image on video exact time?
There is another way to do this using currentTime and defining a function for everytime the time for the video changes. Set a div on top of HTML5 video element and modify that element when currentTime is between 3 and 4 seconds.
Sample code would look like this:
HTML:
<div id="wrap_video">
<div id="video_box">
<div id="video_overlays"></div>
<div>
<video id="myVideo" width="320" height="176" controls autoplay>
<source src="https://www.w3schools.com/tags/mov_bbb.mp4" type="video/mp4">
Your browser does not support HTML5 video.
</video>
</div>
</div>
</div>
CSS:
#video_box{
position:relative;
}
#video_overlays {
position:absolute;
width:160px;
min-height:100px;
background:transparent;
z-index:300000;
bottom:50px;
left:10px;
text-align:center;
}
JavaScript:
var vid = document.getElementById("myVideo");
// Assign an ontimeupdate event to the video element, and execute a function if the current playback position has changed
vid.ontimeupdate = function() {myFunction()};
function myFunction() {
//if currentTime is greater than 3 but less than 4, as in second 3+, go into if statement, otherwise go to else statement
if (vid.currentTime > 3 && vid.currentTime < 4) {
document.getElementById("video_overlays").innerHTML = '<p>Image Here</p>';
}
else {
document.getElementById("video_overlays").innerHTML = '';
}
}
You can inside the first if statement pause the video and then show an image and then make the video start again after a certain timeout, if that was your intention but this code is essentially the skeleton to do something like that.
Working sample: http://jsfiddle.net/l33tstealth/wgcbcf1t/10/
The loadeddata event is fired when the frame at the current playback position of the media has finished loading; often the first frame.
First frame is:
const video = document.querySelector('video');
video.addEventListener('loadeddata', (event) => {
console.log('Yay! The readyState just increased to ' +
'HAVE_CURRENT_DATA or greater for the first time.');
});
You can relay on timeUpdate and get the current time in the event:
event?.detail?.target?.currentTime

How can I get a video to only loop once

How can I get an html5 video to loop only once?
I'm using the html video tag and my video lasts for ten seconds.
I need 20 seconds of video playback.
You could set the autoplay to true and then call play() on the element when it reaches the end. Perhaps not the best approach but I think it should work, this example is using jQuery:
$(document).ready(function() {
// set this variable to the number of times you like to loop the video
var loop = 1;
// this event will be invoked when the media has reached the end
$('#video').on('ended', function() {
// check if we should replay the media
if(loop--) {
// replay the video
this.play();
}
});
});
The markup should be something like this:
<video src="test.ogg" id="video" autoplay></video>

Mission : Create a connection with a list of videos and a canvas make them plays 1 by 1, without stops, no click function, ended AddEventListener

I would like to make a video playlist, (Source video is copied in a canvas to block controls,
options like download video), i made 2 Drag and Drop lists and i would like to connect 1 drag n Drop list to the canvas (that is also the video player box) then play the videos 1 by 1,
witout stops, no click function, function AddEventListener ended in the canvas box.
I worth for 2 videos, here is some parts of the code :
<script type="text/javascript">
// listener function changes src
function myNewSrc() {
var myVideo = document.getElementsByTagName('video')[0];
myVideo.src = "videos/80s_Mix_II-700.mp4";
myVideo.src = "videos/80s_Mix_II-700.webm";
myVideo.load();
myVideo.play();
}
// add a listener function to the ended event
function myAddListener() {
var myVideo = document.getElementsByTagName('video')[0];
myVideo.addEventListener('ended', myNewSrc, false);
}
</script>
<body onload="myAddListener()">
<div id="video_player_box">
<video id="video" autoplay autobuffer width="1" height="1" >
<source src="videos/milenio_6_minimix_700.mp4" type="video/mp4">
<source src="videos/milenio_6_minimix_700.webm" type="video/webm">
<source src="videos/milenio_6_minimix_700.ogg" type="video/ogg">
</video>
<div align="center">
<canvas id="myCanvas" width="1130" height="560">
Your browser does not support the HTML5 canvas tag.</canvas>
</div>
<script>
var v = document.getElementById("video");
var c = document.getElementById("myCanvas");
ctx = c.getContext('2d');
v.addEventListener('play', function() {
var i = window.setInterval(function()
{
ctx.drawImage(v, 5, 5, 1130, 560)
}, 20);
}, false);
v.addEventListener('pause', function() {
window.clearInterval(i);
}, false);
v.addEventListener('ended', function() {
clearInterval(i);
}, false);
</script>
</div>
<div id="cadre2" ondrop="drop(event)" ondragover="allowDrop(event)"> <p> Canal VIP </p>
<ol><li> <video src="videos/milenio_6_minimix_700.mp4" draggable="true"
ondragstart="drag(event)" id="drag1" width="288" height="188" alt="Video 1">
</video></li>
...
The idea is to say get the videos from #cadre2, play them, 1 by 1, in the canvas until the end and loop, make the same path.
I made my list Drag and drop to have the decision to modify online the video playlist, more flexible.
Hope to have some advises!! I'm not pro of Php and dynamic, i've started Javascript but
it takes time to be pro!
If you have some code, it will be really appreciated!! Thanks in advance!!!
In order to make a player that can play videos continously you need to implement some sort of double-buffering at load level (I'll demonstrate later).
But there are some issues in the code as it is -
myVideo.src = "videos/80s_Mix_II-700.mp4";
myVideo.src = "videos/80s_Mix_II-700.webm";
myVideo.load();
This will simply override the source property. And setting the source will automatically start loading the video.
The proper way to check for video support is using the method canPlayType like this:
/// which format can we play?
if (video.canPlayType("video/mp4").length > 0) {
video.src = urlToMP4;
} else if (video.canPlayType("video/webm").length > 0) {
video.src = urlToWEBM;
} else {
video.src = urlToOggOrSomeOtherSupportedFormat;
}
The problem though with canPlayType is that it returns maybe in Chrome and probably in Firefox. It returns an empty string if it cannot play the video type so we check if string contains anything to determine the possibility for this format to play.
You also need to implement an event listener for canplay which tells your app that the video was loaded and buffered successfully and can now be started using play (or starts if autoplay was set to true).
I would recommend a simple procedure like this:
Create an array of objects with the video URLs you want to play for the various formats
When first video is loaded (canplay) start loading the next video in the list when start playing the first
I would also recommend a re-factoring of the code to handle loading and playing.
For example, if we initialize an array to hold our custom video objects:
var list = [];
we can now add URLs like this:
function addVideo(urlMP4, url) {
list.push({
urlMP4: urlMP4,
url: url,
isReady: false
})
}
Then this function will let us add a MP4 and a link for WEBM or OGG:
addVideo('http://video1.mp4', 'http://video1.webm');
addVideo('http://video2.mp4', 'http://video2.webm');
addVideo('http://video3.mp4', 'http://video3.ogg');
...
Then we need to start a "chain-reaction" so to speak by using a double-buffered loading mechanism. The first time we need to trigger it manually:
getVideo(list[0], play);
function getVideo(vid, callback) {
/// which video is playing? (see demo for details)
var video = (isVideo1 === false ? video1 : video2),
me = this;
/// we need to know when video is ready
video.addEventListener('canplay', canPlay, false);;
/// call this when ready
function canPlay(e) {
/// remove event listener (in case setting new src does not trigger)
video.removeEventListener('canplay', canPlay, false);
/// update our object with useful data, for example:
vid.isReady = true;
/// if we provided a callback then call that with custom video object
if (typeof callback === 'function')
callback(vid);
}
/// check video format support (see demo for details)
if (video.canPlayType("video/mp4").length > 0) {
video.src = vid.urlMP4;
} else {
video.src = vid.url;
}
}
Our play function will manage which video is playing and what to play next:
function play(){
/// what video is currently not playing?
var video = (isVideo1 === false ? video1 : video2),
next = current; /// current is index for list, starts at 0
/// switch
isVideo1 = !isVideo1;
/// increment for next video to platy and start over if list ended
next++;
if (next > list.length - 1) next = 0;
/// only attempt next if there are more videos than 1 in list
if (list.length > 0) getVideo(list[next]);
/// start already loaded video (getVideo)
video.play();
isPlaying = true;
/// set current to next in list
current = next;
}
Here is an online demo
I made this demo just to demonstrate the double-buffered loading. Feel free to incorporate in your own project with pause, stop etc.
There is room to move things around in the code I provided here but it's as said just example of the principle. You also need to consider a scenario where next video takes longer to load then what current video playing takes to play (ie. current video ends before next has finished loading). This is not checked in this code.
In order to properly synchronize video frames with canvas you need to use requestAnimationFrame or you will get freezes from time to time.
In the demo the loop runs all the time. You can consider to implement a conditional to stop the loop. I just implemented a conditional for drawing when video list has started playing (rAF does not use much resources in it self and you may get problems synchronizing stop and start when you switch videos so I would personally leave it running as-is for this type of scenarios (continuous video play) and only stop it if there is an error occurring).

HTML5 Audio stop function

I am playing a small audio clip on click of each link in my navigation
HTML Code:
<audio tabindex="0" id="beep-one" controls preload="auto" >
<source src="audio/Output 1-2.mp3">
<source src="audio/Output 1-2.ogg">
</audio>
JS code:
$('#links a').click(function(e) {
e.preventDefault();
var beepOne = $("#beep-one")[0];
beepOne.play();
});
It's working fine so far.
Issue is when a sound clip is already running and i click on any link nothing happens.
I tried to stop the already playing sound on click of link, but there is no direct event for that in HTML5's Audio API
I tried following code but it's not working
$.each($('audio'), function () {
$(this).stop();
});
Any suggestions please?
Instead of stop() you could try with:
sound.pause();
sound.currentTime = 0;
This should have the desired effect.
first you have to set an id for your audio element
in your js :
var ply = document.getElementById('player');
var oldSrc = ply.src;// just to remember the old source
ply.src = "";// to stop the player you have to replace the source with nothing
I was having same issue. A stop should stop the stream and onplay go to live if it is a radio. All solutions I saw had a disadvantage:
player.currentTime = 0 keeps downloading the stream.
player.src = '' raise error event
My solution:
var player = document.getElementById('radio');
player.pause();
player.src = player.src;
And the HTML
<audio src="http://radio-stream" id="radio" class="hidden" preload="none"></audio>
Here is my way of doing stop() method:
Somewhere in code:
audioCh1: document.createElement("audio");
and then in stop():
this.audioCh1.pause()
this.audioCh1.src = 'data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAVFYAAFRWAAABAAgAZGF0YQAAAAA=';
In this way we don`t produce additional request, the old one is cancelled and our audio element is in clean state (tested in Chrome and FF) :>
This method works:
audio.pause();
audio.currentTime = 0;
But if you don't want to have to write these two lines of code every time you stop an audio you could do one of two things. The second I think is the more appropriate one and I'm not sure why the "gods of javascript standards" have not made this standard.
First method: create a function and pass the audio
function stopAudio(audio) {
audio.pause();
audio.currentTime = 0;
}
//then using it:
stopAudio(audio);
Second method (favoured): extend the Audio class:
Audio.prototype.stop = function() {
this.pause();
this.currentTime = 0;
};
I have this in a javascript file I called "AudioPlus.js" which I include in my html before any script that will be dealing with audio.
Then you can call the stop function on audio objects:
audio.stop();
FINALLY CHROME ISSUE WITH "canplaythrough":
I have not tested this in all browsers but this is a problem I came across in Chrome. If you try to set currentTime on an audio that has a "canplaythrough" event listener attached to it then you will trigger that event again which can lead to undesirable results.
So the solution, similar to all cases when you have attached an event listener that you really want to make sure it is not triggered again, is to remove the event listener after the first call. Something like this:
//note using jquery to attach the event. You can use plain javascript as well of course.
$(audio).on("canplaythrough", function() {
$(this).off("canplaythrough");
// rest of the code ...
});
BONUS:
Note that you can add even more custom methods to the Audio class (or any native javascript class for that matter).
For example if you wanted a "restart" method that restarted the audio it could look something like:
Audio.prototype.restart= function() {
this.pause();
this.currentTime = 0;
this.play();
};
It doesn't work sometimes in chrome,
sound.pause();
sound.currentTime = 0;
just change like that,
sound.currentTime = 0;
sound.pause();
From my own javascript function to toggle Play/Pause - since I'm handling a radio stream, I wanted it to clear the buffer so that the listener does not end up coming out of sync with the radio station.
function playStream() {
var player = document.getElementById('player');
(player.paused == true) ? toggle(0) : toggle(1);
}
function toggle(state) {
var player = document.getElementById('player');
var link = document.getElementById('radio-link');
var src = "http://192.81.248.91:8159/;";
switch(state) {
case 0:
player.src = src;
player.load();
player.play();
link.innerHTML = 'Pause';
player_state = 1;
break;
case 1:
player.pause();
player.currentTime = 0;
player.src = '';
link.innerHTML = 'Play';
player_state = 0;
break;
}
}
Turns out, just clearing the currentTime doesn't cut it under Chrome, needed to clear the source too and load it back in. Hope this helps.
As a side note and because I was recently using the stop method provided in the accepted answer, according to this link:
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events
by setting currentTime manually one may fire the 'canplaythrough' event on the audio element. In the link it mentions Firefox, but I encountered this event firing after setting currentTime manually on Chrome. So if you have behavior attached to this event you might end up in an audio loop.
shamangeorge wrote:
by setting currentTime manually one may fire the 'canplaythrough' event on the audio element.
This is indeed what will happen, and pausing will also trigger the pause event, both of which make this technique unsuitable for use as a "stop" method. Moreover, setting the src as suggested by zaki will make the player try to load the current page's URL as a media file (and fail) if autoplay is enabled - setting src to null is not allowed; it will always be treated as a URL. Short of destroying the player object there seems to be no good way of providing a "stop" method, so I would suggest just dropping the dedicated stop button and providing pause and skip back buttons instead - a stop button wouldn't really add any functionality.
This approach is "brute force", but it works assuming using jQuery is "allowed". Surround your "player" <audio></audio> tags with a div (here with an id of "plHolder").
<div id="plHolder">
<audio controls id="player">
...
</audio>
<div>
Then this javascript should work:
function stopAudio() {
var savePlayer = $('#plHolder').html(); // Save player code
$('#player').remove(); // Remove player from DOM
$('#FlHolder').html(savePlayer); // Restore it
}
I was looking for something similar due to making an application that could be used to layer sounds with each other for focus. What I ended up doing was - when selecting a sound, create the audio element with Javascript:
const audio = document.createElement('audio') as HTMLAudioElement;
audio.src = getSoundURL(clickedTrackId);
audio.id = `${clickedTrackId}-audio`;
console.log(audio.id);
audio.volume = 20/100;
audio.load();
audio.play();
Then, append child to document to actually surface the audio element
document.body.appendChild(audio);
Finally, when unselecting audio, you can stop and remove the audio element altogether - this will also stop streaming.
const audio = document.getElementById(`${clickedTrackId}-audio`) as HTMLAudioElement;
audio.pause();
audio.remove();
If you have several audio players on your site and you like to pause all of them:
$('audio').each( function() {
$(this)[0].pause();
});
I believe it would be good to check if the audio is playing state and reset the currentTime property.
if (sound.currentTime !== 0 && (sound.currentTime > 0 && sound.currentTime < sound.duration) {
sound.currentTime = 0;
}
sound.play();
for me that code working fine. (IE10+)
var Wmp = document.getElementById("MediaPlayer");
Wmp.controls.stop();
<object classid="clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6"
standby="Loading áudio..." style="width: 100%; height: 170px" id="MediaPlayer">...
Hope this help.
What I like to do is completely remove the control using Angular2 then it's reloaded when the next song has an audio path:
<audio id="audioplayer" *ngIf="song?.audio_path">
Then when I want to unload it in code I do this:
this.song = Object.assign({},this.song,{audio_path: null});
When the next song is assigned, the control gets completely recreated from scratch:
this.song = this.songOnDeck;
The simple way to get around this error is to catch the error.
audioElement.play() returns a promise, so the following code with a .catch() should suffice manage this issue:
function playSound(sound) {
sfx.pause();
sfx.currentTime = 0;
sfx.src = sound;
sfx.play().catch(e => e);
}
Note: You may want to replace the arrow function with an anonymous function for backward compatibility.
In IE 11 I used combined variant:
player.currentTime = 0;
player.pause();
player.currentTime = 0;
Only 2 times repeat prevents IE from continuing loading media stream after pause() and flooding a disk by that.
What's wrong with simply this?
audio.load()
As stated by the spec and on MDN, respectively:
Playback of any previously playing media resource for this element stops.
Calling load() aborts all ongoing operations involving this media element