I have a problem with Javascript:
I want to stop the audio in a Js program when the game comes to an end. For this I define a variable "endGame" and I make it false, so that when I call it true in an event, the audio stops. But the problem is that the audio does not stop when this variable is set to true.
Here's the piece of code that I need some help with:
let endGame = false
main.addEventListener("click", (e) => {
themeSong.play()
if(endGame == true) {
themeSong.pause()
}
EventListeners will not be triggered when the variable change. You should do it manually.
let endGame = false;
main.addEventListener("click", (e) => {
themeSong.play();
});
// The function which updates the endGame variable and stop the audio
function fnEndGame() {
endGame = true;
themeSong.pause();
}
// You should call fnEndGame() to stop the game, DO NOT update the variable directly.
By using a custom event, and then throwing that event whenever you want, you can pause your music execution the moment you want to.
So instead of using an "endgame" variable, I suggest you throw a custom event named "supergame_endgame" and listen to that event, and halt all relevant audio's in that event listener.
See my comments in the code below for more explanation.
let audio = document.querySelector('audio');
let start = document.getElementById('play');
let stop = document.getElementById('stop');
/** simple starter of the sound **/
start.addEventListener('click', () => {
audio.play();
});
/** Event handler for own custom event, named `stop_my_audio_play`
* This will actually stop audio play, regardless from where the event `stop_my_audio_play` is
* cast from
*/
window.addEventListener('stop_my_audio_play', (e) => {
audio.pause();
});
/** Throw an event `stop_my_audio_play` with the attribute bubbles set to true
* So it can be caught at window level
*/
stop.addEventListener('click', () => {
stop.dispatchEvent(new Event('stop_my_audio_play', {bubbles:true}))
});
audio {
display:none;
}
<audio controls="controls" autobuffer="autobuffer" autoplay="autoplay" loop>
<source src="data:audio/wav;base64,UklGRhwMAABXQVZFZm10IBAAAAABAAEAgD4AAIA+AAABAAgAZGF0Ya4LAACAgICAgICAgICAgICAgICAgICAgICAgICAf3hxeH+AfXZ1eHx6dnR5fYGFgoOKi42aloubq6GOjI2Op7ythXJ0eYF5aV1AOFFib32HmZSHhpCalIiYi4SRkZaLfnhxaWptb21qaWBea2BRYmZTVmFgWFNXVVVhaGdbYGhZbXh1gXZ1goeIlot1k6yxtKaOkaWhq7KonKCZoaCjoKWuqqmurK6ztrO7tbTAvru/vb68vbW6vLGqsLOfm5yal5KKhoyBeHt2dXBnbmljVlJWUEBBPDw9Mi4zKRwhIBYaGRQcHBURGB0XFxwhGxocJSstMjg6PTc6PUxVV1lWV2JqaXN0coCHhIyPjpOenqWppK6xu72yxMu9us7Pw83Wy9nY29ve6OPr6uvs6ezu6ejk6erm3uPj3dbT1sjBzdDFuMHAt7m1r7W6qaCupJOTkpWPgHqAd3JrbGlnY1peX1hTUk9PTFRKR0RFQkRBRUVEQkdBPjs9Pzo6NT04Njs+PTxAPzo/Ojk6PEA5PUJAQD04PkRCREZLUk1KT1BRUVdXU1VRV1tZV1xgXltcXF9hXl9eY2VmZmlna3J0b3F3eHyBfX+JgIWJiouTlZCTmpybnqSgnqyrqrO3srK2uL2/u7jAwMLFxsfEv8XLzcrIy83JzcrP0s3M0dTP0drY1dPR1dzc19za19XX2dnU1NjU0dXPzdHQy8rMysfGxMLBvLu3ta+sraeioJ2YlI+MioeFfX55cnJsaWVjXVlbVE5RTktHRUVAPDw3NC8uLyknKSIiJiUdHiEeGx4eHRwZHB8cHiAfHh8eHSEhISMoJyMnKisrLCszNy8yOTg9QEJFRUVITVFOTlJVWltaXmNfX2ZqZ21xb3R3eHqAhoeJkZKTlZmhpJ6kqKeur6yxtLW1trW4t6+us7axrbK2tLa6ury7u7u9u7vCwb+/vr7Ev7y9v8G8vby6vru4uLq+tri8ubi5t7W4uLW5uLKxs7G0tLGwt7Wvs7avr7O0tLW4trS4uLO1trW1trm1tLm0r7Kyr66wramsqaKlp52bmpeWl5KQkImEhIB8fXh3eHJrbW5mYGNcWFhUUE1LRENDQUI9ODcxLy8vMCsqLCgoKCgpKScoKCYoKygpKyssLi0sLi0uMDIwMTIuLzQ0Njg4Njc8ODlBQ0A/RUdGSU5RUVFUV1pdXWFjZGdpbG1vcXJ2eXh6fICAgIWIio2OkJGSlJWanJqbnZ2cn6Kkp6enq62srbCysrO1uLy4uL+/vL7CwMHAvb/Cvbq9vLm5uba2t7Sysq+urqyqqaalpqShoJ+enZuamZqXlZWTkpGSkpCNjpCMioqLioiHhoeGhYSGg4GDhoKDg4GBg4GBgoGBgoOChISChISChIWDg4WEgoSEgYODgYGCgYGAgICAgX99f398fX18e3p6e3t7enp7fHx4e3x6e3x7fHx9fX59fn1+fX19fH19fnx9fn19fX18fHx7fHx6fH18fXx8fHx7fH1+fXx+f319fn19fn1+gH9+f4B/fn+AgICAgH+AgICAgIGAgICAgH9+f4B+f35+fn58e3t8e3p5eXh4d3Z1dHRzcXBvb21sbmxqaWhlZmVjYmFfX2BfXV1cXFxaWVlaWVlYV1hYV1hYWVhZWFlaWllbXFpbXV5fX15fYWJhYmNiYWJhYWJjZGVmZ2hqbG1ub3Fxc3V3dnd6e3t8e3x+f3+AgICAgoGBgoKDhISFh4aHiYqKi4uMjYyOj4+QkZKUlZWXmJmbm52enqCioqSlpqeoqaqrrK2ur7CxsrGys7O0tbW2tba3t7i3uLe4t7a3t7i3tre2tba1tLSzsrKysbCvrq2sq6qop6alo6OioJ+dnJqZmJeWlJKSkI+OjoyLioiIh4WEg4GBgH9+fXt6eXh3d3V0c3JxcG9ubWxsamppaWhnZmVlZGRjYmNiYWBhYGBfYF9fXl5fXl1dXVxdXF1dXF1cXF1cXF1dXV5dXV5fXl9eX19gYGFgYWJhYmFiY2NiY2RjZGNkZWRlZGVmZmVmZmVmZ2dmZ2hnaGhnaGloZ2hpaWhpamlqaWpqa2pra2xtbGxtbm1ubm5vcG9wcXBxcnFycnN0c3N0dXV2d3d4eHh5ent6e3x9fn5/f4CAgIGCg4SEhYaGh4iIiYqLi4uMjY2Oj5CQkZGSk5OUlJWWlpeYl5iZmZqbm5ybnJ2cnZ6en56fn6ChoKChoqGio6KjpKOko6SjpKWkpaSkpKSlpKWkpaSlpKSlpKOkpKOko6KioaKhoaCfoJ+enp2dnJybmpmZmJeXlpWUk5STkZGQj4+OjYyLioqJh4eGhYSEgoKBgIB/fn59fHt7enl5eHd3dnZ1dHRzc3JycXBxcG9vbm5tbWxrbGxraWppaWhpaGdnZ2dmZ2ZlZmVmZWRlZGVkY2RjZGNkZGRkZGRkZGRkZGRjZGRkY2RjZGNkZWRlZGVmZWZmZ2ZnZ2doaWhpaWpra2xsbW5tbm9ub29wcXFycnNzdHV1dXZ2d3d4eXl6enp7fHx9fX5+f4CAgIGAgYGCgoOEhISFhoWGhoeIh4iJiImKiYqLiouLjI2MjI2OjY6Pj46PkI+QkZCRkJGQkZGSkZKRkpGSkZGRkZKRkpKRkpGSkZKRkpGSkZKRkpGSkZCRkZCRkI+Qj5CPkI+Pjo+OjY6Njo2MjYyLjIuMi4qLioqJiomJiImIh4iHh4aHhoaFhoWFhIWEg4SDg4KDgoKBgoGAgYCBgICAgICAf4CAf39+f35/fn1+fX59fHx9fH18e3x7fHt6e3p7ent6e3p5enl6enl6eXp5eXl4eXh5eHl4eXh5eHl4eXh5eHh3eHh4d3h4d3h3d3h4d3l4eHd4d3h3eHd4d3h3eHh4eXh5eHl4eHl4eXh5enl6eXp5enl6eXp5ent6ent6e3x7fHx9fH18fX19fn1+fX5/fn9+f4B/gH+Af4CAgICAgIGAgYCBgoGCgYKCgoKDgoOEg4OEg4SFhIWEhYSFhoWGhYaHhoeHhoeGh4iHiIiHiImIiImKiYqJiYqJiouKi4qLiouKi4qLiouKi4qLiouKi4qLi4qLiouKi4qLiomJiomIiYiJiImIh4iIh4iHhoeGhYWGhYaFhIWEg4OEg4KDgoOCgYKBgIGAgICAgH+Af39+f359fn18fX19fHx8e3t6e3p7enl6eXp5enl6enl5eXh5eHh5eHl4eXh5eHl4eHd5eHd3eHl4d3h3eHd4d3h3eHh4d3h4d3h3d3h5eHl4eXh5eHl5eXp5enl6eXp7ent6e3p7e3t7fHt8e3x8fHx9fH1+fX59fn9+f35/gH+AgICAgICAgYGAgYKBgoGCgoKDgoOEg4SEhIWFhIWFhoWGhYaGhoaHhoeGh4aHhoeIh4iHiIeHiIeIh4iHiIeIiIiHiIeIh4iHiIiHiIeIh4iHiIeIh4eIh4eIh4aHh4aHhoeGh4aHhoWGhYaFhoWFhIWEhYSFhIWEhISDhIOEg4OCg4OCg4KDgYKCgYKCgYCBgIGAgYCBgICAgICAgICAf4B/f4B/gH+Af35/fn9+f35/fn1+fn19fn1+fX59fn19fX19fH18fXx9fH18fXx9fH18fXx8fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x7fHt8e3x8e3x7fHt8e3x7fHx8fXx9fH18fX5+fX59fn9+f35+f35/gH+Af4B/gICAgICAgICAgICAgYCBgIGAgIGAgYGBgoGCgYKBgoGCgYKBgoGCgoKDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KDgoOCg4KCgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGCgYKBgoGBgYCBgIGAgYCBgIGAgYCBgIGAgYCBgIGAgYCBgIGAgYCAgICBgIGAgYCBgIGAgYCBgIGAgYCBgExJU1RCAAAASU5GT0lDUkQMAAAAMjAwOC0wOS0yMQAASUVORwMAAAAgAAABSVNGVBYAAABTb255IFNvdW5kIEZvcmdlIDguMAAA" />
</audio>
<button id="play">Play</button>
<button id="stop">Stop</button>
Related
I have two media elements:
<audio id="aud"></audio>
<video id="vid"></video>
I've attached media urls to both of them.
Then I load and play them by doing audio.load() and video.load() followed by audio.play() and video.play() in listener
audio.load();
video.load();
video.addEventListener("canplay", () => {
video.play();
});
audio.addEventListener("canplay", () => {
audio.play();
});
Both start loading, audio loads first and it takes around 1-2 seconds to video to load.
When they actually start playing depends on when "canplay" event is fired by each media elements. (which is fired after loading is done for at-least a couple of frames)
what I wanna do is, call audio.play() AND video.play() together ONLY when "canplay" event is fired for both of them. How can I achieve this?
note that I DO need to use both audio and video element, and i'm trying to achieve a close but not perfect synchronisation at start.
in brief: what i want to achieve is:
audio.load();
video.load();
// when video can "canplay" and when audio can "canplay" ONLY then
video.play();
audio.play();
Use a global flags:
let audCanPlay=false;
let vidCanPlay=false;
function audPlay() {
audCanPlay=true;
console.log("audCanPlay=true");
if(vidCanPlay) playBoth();
}
function vidPlay() {
vidCanPlay=true;
console.log("vidCanPlay=true");
if(audCanPlay) playBoth();
}
function playBoth() {
console.log("audio.play();");
console.log("video.play();");
}
/* commented for demonstration
audio.addEventListener("canplay", audPlay);
video.addEventListener("canplay", vidPlay);
audio.load();
video.load();
*/
<audio id="aud"></audio>
<button id="audBtn" onclick="audPlay()">Mimic Audio canplay</button>
<video id="vid"></video>
<button id="vidBtn" onclick="vidPlay()">Mimic Video canplay</button>
I think it's better to add the event listeners before calling the load methods.
I think it's better to do it this way, rather than waiting for one to finish/canplay and only then call the second = saves time/faster.
Promises resolved by canplay events and combined with Promise.all to wait for multiple events come to mind.
However, how complex it needs to be also depends on how general purpose it is to be. For the simple case of waiting for exactly one video and one audio to be ready to play, I would only use the one promise and count events as they arrive.
Note that to catch events on elements that retrieve files it is unsafe to set src or href attributes before adding event listeners.
Concept code (untested) to add listeners, set sources and return a promise when both are ready:
const loadAV = (video, vidSrc, audio, audioSrc, timeout) {
let resolve, reject;
const p = new Promise( (r,j) => { resolve = r, reject = j};
let count = 0;
let timer;
const listenCanPlay = () => {
if( ++count == 2) {
resolve();
unListen();
}
};
const unListen = () => {
video.removeEventListener("canplay", listenCanPlay);
audio.removeEventListener("canplay", listenCanPlay);
if( timeout) {
clearTimeout(timer);
}
};
if( timeout) {
timer = setTimeout( ()=> {
if( count < 2) {
unListen();
reject( new Error(`loadAV timed out after ${timeout} msec`)
}
}, timeout);
}
video.addEventListener("canplay", listenCanPlay);
audio.addEventListener("canplay", listenCanPlay);
video.src = vidSrc;
audio.src = audioSrc;
return p;
}
which would then be called like
loadAV( video, video_url, audio, audio_url, 15000)
.then(()=>{ video.play(); audio.play()})
.catch(err=> console.log(err));
I use standart video html5 player in my react app:
const Video = ({ url, contentType }) => (
<video poster={url}>
<source src={`${url}#t=0.01`} type={contentType} className="video" />
</video>
)
and i would like to hide controls and play/pause video by clicking on it, i found how to do it by using javascript:
var overlay = document.getElementById('video-overlay');
var video = document.getElementById('video');
var videoPlaying = false;
overlay.onclick = function() {
if (videoPlaying) {
video.pause();
videoPlaying = false;
}
else {
video.play();
videoPlaying = true;
}
}
but i don't know how to replace it with react, i tried to add onClick{e => e.play} but it didn't work, also i checked what e contains, it doesn't contain any isPlaying flags or play/pause possibililties, please tell me how can i to it properly ?
There are two reasons that wouldn't work:
You didn't call play, you just referred to it. To call it, you'd need ().
The argument passed to an event handler isn't the element the event occurred on, it's an Event object (in React's case, a synthetic one). You can access the element you hooked the event on via the event's currentTarget property:
<video poster={url} onClick={e => e.currentTarget.play()}>
or with destructuring
<video poster={url} onClick={({currentTarget}) => currentTarget.play()}>
I would like to play 2 html video exactly at the same time (side-by-side).
I made a button which has a click EventListener on it. This listener trigger the 2 <video> tag to play it, but I think there is 150 ms a delay at the second play trigger.
index.html
<section>
<video src="source1" id="video1"></video>
<video src="source2" id="video2"></video>
<button id="play">Play</button>
</section>
and here is the basic script.js file.
const $video1 = document.getElementById('video1');
const $video2 = document.getElementById('video2');
const $play = document.getElementById('play');
const playVideo = () => {
$video1.play();
$video2.play();
};
$play.addEventListener('click', playVideo);
These 2 videos are almost the same, except the first video size is about 12MB and the other one is around 20MB
What I tried:
Tried to add a console.time('video') that could logs the delay between each play function call, but that can be different in each environment.
The only way I'm aware of about playing video in sync is to emit a custom event to sync the video in requestAnimationFrame:
var videos = {
a: Popcorn("#a"),
b: Popcorn("#b"),
},
scrub = $("#scrub"),
loadCount = 0,
events = "play pause timeupdate seeking".split(/\s+/g);
// iterate both media sources
Popcorn.forEach(videos, function(media, type) {
// when each is ready...
media.on("canplayall", function() {
// trigger a custom "sync" event
this.emit("sync");
// set the max value of the "scrubber"
scrub.attr("max", this.duration());
// Listen for the custom sync event...
}).on("sync", function() {
// Once both items are loaded, sync events
if (++loadCount == 2) {
// Iterate all events and trigger them on the video B
// whenever they occur on the video A
events.forEach(function(event) {
videos.a.on(event, function() {
// Avoid overkill events, trigger timeupdate manually
if (event === "timeupdate") {
if (!this.media.paused) {
return;
}
videos.b.emit("timeupdate");
// update scrubber
scrub.val(this.currentTime());
return;
}
if (event === "seeking") {
videos.b.currentTime(this.currentTime());
}
if (event === "play" || event === "pause") {
videos.b[event]();
}
});
});
}
});
});
scrub.bind("change", function() {
var val = this.value;
videos.a.currentTime(val);
videos.b.currentTime(val);
});
// With requestAnimationFrame, we can ensure that as
// frequently as the browser would allow,
// the video is resync'ed.
function sync() {
if (videos.b.media.readyState === 4) {
videos.b.currentTime(
videos.a.currentTime()
);
}
requestAnimationFrame(sync);
}
sync();
html{font-family:arial;}
<script src="https://static.bocoup.com/js/popcorn.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<video height="180" width="300" id="a" controls>
<source src="https://videos.mozilla.org/serv/webmademovies/popcorntest.mp4"></source>
<source src="https://videos.mozilla.org/serv/webmademovies/popcorntest.ogv"></source>
<source src="https://videos.mozilla.org/serv/webmademovies/popcorntest.webm"></source>
</video>
<video height="180" width="300" id="b">
<source src="https://videos.mozilla.org/serv/webmademovies/popcorntest.mp4"></source>
<source src="https://videos.mozilla.org/serv/webmademovies/popcorntest.ogv"></source>
<source src="https://videos.mozilla.org/serv/webmademovies/popcorntest.webm"></source>
</video>
<input type="range" value="0" id="scrub" />
Ref: https://bocoup.com/blog/html5-video-synchronizing-playback-of-two-videos
UPDATE
Both of the answers was correct.
What I didn't know, that one of the video file has sound, and the other don't. That caused the 150ms delay.
Solved it with ffmpeg. Create an .mp3 file with the sound (and play it with <audio>), and 2 .mp4 files without sound.
They are playing exactly at the same time now.
I will also add this link here, as evident someone had a very similar problem, albeit with audio
Run function after audio is loaded
It appears you are looking for this event listener "canplaythrough", check more in the link.
function checkAudio() {
audioElem.setAttribute("src", audioFile);
audioElem.addEventListener('canplaythrough', (event) => {
resultScreen.innerHTML = "loaded audio";
});
}
I have used javascript Audio() before, but now I need to add some reverb effect in the audio and I am using reverb.js which uses the AudioContext api. I have the start property available, but no pause property? How do I pause or stop the audio??
Here is my code:
<script src="http://reverbjs.org/reverb.js"></script>
<script>
// 1) Setup your audio context (once) and extend with Reverb.js.
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
reverbjs.extend(audioContext);
// 2) Load the impulse response; upon load, connect it to the audio output.
var reverbUrl = "http://reverbjs.org/Library/SaintLawrenceChurchMolenbeekWersbeekBelgium.m4a";
var reverbNode = audioContext.createReverbFromUrl(reverbUrl, function() {
reverbNode.connect(audioContext.destination);
});
// 3) Load a test sound; upon load, connect it to the reverb node.
var sourceUrl = "./sample.mp3";
var sourceNode = audioContext.createSourceFromUrl(sourceUrl, function() {
sourceNode.connect(reverbNode);
});
</script>
Play
Stop
Also, I tried using stop(), and it works, but when I fire start() after clicking on stop, the start() doesn't work. Can you you help me out with a solution??
You can use the suspend() and resume() methods of AudioContext to pause and resume your audio: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/suspend
One way to implement this with a single button for play/pause/resume, would be to add a function that controls the player state. For example:
let started = false;
function pauseOrResume() {
if (!started) {
sourceNode.start();
started = true;
document.getElementById("pauseButton").innerHTML = 'Pause';
} else if (audioContext.state === 'running') {
audioContext.suspend().then(function () {
document.getElementById("pauseButton").innerHTML = 'Resume';
});
} else if (audioContext.state === 'suspended') {
audioContext.resume().then(function () {
document.getElementById("pauseButton").innerHTML = 'Pause';
});
}
}
And replace your existing "Play" button with:
<a id="pauseButton" href="javascript:pauseOrResume()">Play</a>
This does the following:
If the audio hasn't yet been started, the link will say "Play".
If the user clicks "Play", the audio will start playing and the text of the link will change to "Pause".
If the user clicks "Pause" while the audio is playing, it will be paused, and the text of the link will change to "Resume".
I have read previous posts on this and documents by microsoft but cannot seem to get my app to run Sound in the background. It plays 100% but when ever the app is then suspended the music also stops. I have added "Background Tasks" declarations selecting Audio and my audio tag looks like this
<audio id="musicplayr" msAudioCategory="BackgroundCapableMedia" controls="controls"><source src="song.mp3"/> </audio
and finally my javascript includes the references to MediaControls
var MediaControls = Windows.Media.MediaControl;
// Add event listeners for the buttons
MediaControls.addEventListener("playpressed", play, false);
MediaControls.addEventListener("pausepressed", pause, false);
MediaControls.addEventListener("playpausetogglepressed", playpausetoggle, false);
// Add event listeners for the audio element
document.getElementById("musicplayr").addEventListener("playing", playing, false);
document.getElementById("musicplayr").addEventListener("paused", paused, false);
document.getElementById("musicplayr").addEventListener("ended", ended, false);
and below in the code i have the event handlers
// Define functions that will be the event handlers
function play() {
document.getElementById("musicplayr").play();
}
function pause() {
document.getElementById("musicplayr").pause();
}
function playpausetoggle() {
if(MediaControls.isPlaying === true) {
document.getElementById("musicplayr").pause();
} else {
document.getElementById("musicplayr").play();
}
}
function playing() {
Windows.Media.MediaControl.isPlaying = true;
}
function paused() {
Windows.Media.MediaControl.isPlaying = false;
}
function ended() {
Windows.Media.MediaControl.isPlaying = false;
}
*Note musicplayr is the reference for the html5 tag
Any help appreciated why this is not working?
You also need an event handler for the stoppressed event. Without any of the four handlers--playpressed, pausepressed, playpausetogglepressed, and stoppressed--background audio won't be enabled. See http://social.msdn.microsoft.com/Forums/en-IN/winappswithhtml5/thread/2ca0c122-df31-401c-a444-2149dd3e8d68 on the MSDN forums where the same problem was raised.
.Kraig