I'm trying to play video1 to 3 without playing the video twice. I'm using videos.shift(); to remove the first video from the array, but I'm not sure how to remove the other 2 videos after they play once.
var videos = ["video1", "video2", "video3"];
videos.shift();
var player1 = document.getElementById("video");
function setMp4Source1() {
var currentVideoIndex = Math.floor(Math.random() * videos.length);
var currentVideo = "videos/" + videos[currentVideoIndex] + ".mp4";
player1.src = currentVideo;
player1.load();
player1.play();
}
player1.addEventListener('ended', nextVideo, false);
function nextVideo() {
setMp4Source1();
}
If I understand this correctly, videos is a queue of videos that need to be played in order, then nothing else.
I would normalize all the queue management into the nextVideo() function, so that there was nothing special about the first time you play. Thus:
var videos = ["video1", "video2", "video3"]
var player1 = document.getElementById("video")
function setMp4Source1(theVideo) {
var currentVideo = "videos/" + theVideo + ".mp4"
player1.src = currentVideo
player1.load()
player1.play()
}
player1.addEventListener('ended', nextVideo, false)
function nextVideo() {
let theVideo = videos.unshift()
setMp4Source1(theVideo)
}
nextVideo() // start the chain here!
Now, this doesn't, as in your initial example, play a random video - just the next in the queue. You can play a random video and remove it from the queue by using splice() in nextVideo() as such:
function nextVideo() {
let randomIndex = Math.floor(Math.random() * videos.length)
let theVideo = videos[randomIndex]
videos.splice(randomIndex,1) // remove 1 element starting at the index
setMp4Source1(theVideo)
}
Of course, next you'll want to add a check to make sure you're not accessing an empty array...
function nextVideo() {
if( videos.length < 1) {
// no moar videos! :(
// probably best to bail out here and avoid further chaining.
// if you're clever, you'll add a placeholder image to let
// the user know there's no more videos.
// maybe something from http://placekitten.com/
player1.removeEventListener('ended', nextVideo, false)
sizer_x = player1.clientWidth
sizer_y = player1.clientHeight
const kitty = '<img src="http://placekitten.com/' + sizer_x + '/' + sizer_y + '"/>'
const kittyImg = document.createElement(kitty)
player1.parentNode.replaceChild(kittyImg, player1)
return
}
let randomIndex = Math.floor(Math.random() * videos.length)
let theVideo = videos[randomIndex]
videos.splice(randomIndex,1) // remove 1 element starting at the index
setMp4Source1(theVideo)
}
Keep track of the index numbers played with a Set object. Set object is simular to an array but will only contain unique items. Details are commented in the following Snippet.
const videos = [
"vs8s3.mp4", "vs12s3.mp4", "vs21s3.mp4", "vs2s3.mp4"
];
const player = document.querySelector("video");
// Create a Set object
const played = new Set();
function playVideo() {
let current = Math.floor(Math.random() * videos.length);
const base = 'https://glpjt.s3.amazonaws.com/so/av/';
const source = base + videos[current];
/* Check if video has already been played */
// If Set {played} DOES NOT have the {current} index number...
if (!played.has(current)) {
// Add {current} index number to Set {played}
played.add(current);
player.src = source;
player.play();
}
/* or if Set {played} does have {current} index number AND Set {played}
DOES NOT have the same amount of numbers as Array {videos}... */
else if (played.size != videos.length) {
// Run the function again
playVideo();
}
// Otherwise end function
else {
return false;
}
/* OPTIONAL: Display the indexes of each video played in order */
console.clear();
console.log(JSON.stringify(Array.from(played)));
};
player.addEventListener('ended', playVideo, false);
player.addEventListener('click', playVideo, false);
video {
cursor: pointer;
}
<video poster='http://simgbb.com/background/W46xw0TswdDx.jpg' width='360'></video>
Related
We are using videojs.fairplay.js for playing DRM content in MAC safari browser. At first while playing DRM video we get one standard playlist (.m3u8) that has defined stack of segments (.ts). We need to know the each segment information and there complete meta information like (length, type, url, etc..).
var video = videojs("my-video");
video.on('playing', function () {
console.log("The video has been playing");
var segment = get_current_segment_info(this);
console.log(segment.uri);
});
function get_current_segment_info(obj, old_segment = null) {
var target_media = obj.tech().hls.playlists.media();
var snapshot_time = obj.currentTime();
var segment;
var segment_time;
// Itinerate trough available segments and get first within which snapshot_time is
for (var i = 0, l = target_media.segments.length; i < l; i++) {
// Note: segment.end may be undefined or is not properly set
if (snapshot_time < target_media.segments[i].end) {
segment = target_media.segments[i];
break;
}
}
// Null segment_time in case it's lower then 0.
if (segment) {
segment_time = Math.max(0, snapshot_time - (segment.end - segment.duration));
// Because early segments don't have end property
} else {
segment = target_media.segments[0];
segment_time = 0;
}
console.log(snapshot_time);
console.log(segment.uri);
console.log(segment.resolvedUri);
console.log(segment);
return segment;
}
CodePen
On line 19, the event listener calls an anonymous function, I'd like to break this out into its own function, but I also think I need to be passing a 3rd argument in. possibly the padz class, or maybe the audio tags.. maybe a combination of both, but not sure of the syntax to do that.
JavaScript
var clickCounter = 0;
var currentLevel = 0;
var simon_sSequence = [];
var player_sInput = [];
const audioTapper = document.querySelector("#audioT");
const audioError = document.querySelector("#audioE");
const levelDisplay = document.getElementById("level_Display");
const simonzSequence = document.getElementById("simon_sSequence");
const playerInput = document.getElementById("player_sInput");
const start_Tapper = document.querySelector(".startTapper");
const pads = document.querySelectorAll(".padz");
// Convert the padz list to an array that we can iterate over using .forEach()
Array.from(pads).forEach((pad, index) => {
// Get the associated audio element nested in the padz-div
const audio = pad.querySelector("audio");
// Add a click listener to each pad which
// will play the audio, push the index to
// the user input array, and update the span
pad.addEventListener("click", () => {
player_sInput.push(index);
if (player_sInput[clickCounter] !== simon_sSequence[clickCounter]) {
audioError.play();
$("body").animate({ backgroundColor: "red" }, 100);
$("#container").effect( "shake", {times:100}, 1000, 'linear' );
$("body").animate({ backgroundColor: "gray" }, 5000);
//console.log("wrong");
} else { //end of if
audio.play();
playerInput.textContent = "Player's reply " + player_sInput;
clickCounter++;
//console.log(clickCounter);
} //end of else
}); //end of EventListener
}); //end of Array.from(pads).forEach((pad, index)
start_Tapper.addEventListener("click", (resetStart)); //end of EventListener
function resetStart() {
//generate a random number & push it to simon_sSequence
simon_sSequence.push(Math.floor(Math.random() * 4));
simonzSequence.textContent = "Simon says " + simon_sSequence;
playerInput.textContent = "Player's reply " + player_sInput;
//for each in the array set time interval(300ms);
//dipslay hover effect
//play pad sound
audioTapper.play();
player_sInput = []; //clear player's input for this turn
clickCounter = 0;
currentLevel++;
levelDisplay.textContent = "Level: " + currentLevel + " " + simon_sSequence.length + " " + player_sInput.length;
}
I am fairly new to JavaScript and jQuery, what I'm doing is actually calling a function with an array of some colors, initially it is ['blue', 'green']
And I runs a for-loop on this array to play sound specific to each number.
But the problem is, it just plays sound with the last parameter. Why so?
I want to play both, (or how many I adjust) sounds to be played after a fixed interval (like here I've 1000ms). Thanks in advance.
Here's my code:
function singPattern(compPattern) {
var audio;
for(let i=0; i<compPattern.length; i++) {
audio = new Audio("../../music-files/" + compPattern[i] + ".mp3");
window.setTimeout(function () {
audio.play();
}, 2000);
}
}
function singPattern(compPattern) {
var audio; // There is only one audio
for(let i=0; i<compPattern.length; i++) {
//everytime in this loop audio is assigned to the "Audio"
// the old one is off to the pasture. (dead)(GC)
audio = new Audio("../../music-files/" + compPattern[i] + ".mp3");
window.setTimeout(function () {
audio.play();
}, 2000);
}
}
Like in all things javascript, There are several ways to handle this.
you could pass the object into the timeout like this. and the time out will have a closure of the object.
function singPattern(compPattern) {
for(let i=0; i<compPattern.length; i++) {
// I will just print the word but you can pass the object in.
var audio = compPattern[i] + ".mp3";
window.setTimeout(function (_audio) {
console.log(_audio)
}, 2000, audio);
}
}
If you need access to the Audio object outside of the setTimeout to cancel/replay it you could set up a dictionary like this :
function singPattern(compPattern) {
var audio ={};
for(let i=0; i<compPattern.length; i++) {
audio[compPattern[i]] = compPattern[i] + ".mp3";
}
setTimeout(function(){
for( var a in audio ){ console.log( "play ", a , audio[a] ); }
},2000);
// audio['green'].pause() ?
}
Edit
Heres a working sample with external mp3s
function singPattern(compPattern) {
for (let i = 0; i < compPattern.length; i++) {
var audio = compPattern[i];
setTimeout(function (_audio) {
var sound = new Audio(_audio);
console.log("Playing " + _audio);
sound.play();
}, 2000, audio);
}
}
var soundUrls = ["http://soundbible.com/mp3/Stream Noise-SoundBible.com-866411702.mp3", "http://soundbible.com/mp3/Strange_Days-Mike_Koenig-176042049.mp3"];
singPattern(soundUrls);
When working with audio elements (<audio>) or contexts(AudioContext), you can check their currentTime property to know exactly the play time of your buffer.
All of this is fine an dandy until I created multiple sources (or AudioBufferSourceNode) in a single AudioContext.
The sources could be played at different times, therefore I would need to know their corresponding currentTime's, to illustrate:
Some base code for you to work off:
buffer1 = [0,1,0]; //not real buffers
buffer2 = [1,0,1];
ctx = new AudioContext();
source1 = ctx.createBufferSourceNode();
source1.buffer = buffer1;
source1.connect(ctx.destination);
source1.start(0);
source2 = ctx.createBufferSourceNode();
source2.buffer = buffer2;
source2.connect(ctx.destination);
setTimeout(1000/*some time later*/){
source2.start(0);
}
setTimeout(1500/*some more time later*/){
getCurrentTime();
}
function getCurrentTime(){
/* magic */
/* more magic */
console.log("the sources currentTime values are obviously 1500 (source1) and 500 (source2).");
}
What I usually do is create a wrapper for the audio source node that keeps track of the playback state. I've tried to minimise the code below to show the basics.
The core idea is to keep track of the time the sound is started and the time the sound is 'paused' and use those values to get the current time and to resume playback from the paused position.
I put a working example on codepen
function createSound(buffer, context) {
var sourceNode = null,
startedAt = 0,
pausedAt = 0,
playing = false;
var play = function() {
var offset = pausedAt;
sourceNode = context.createBufferSource();
sourceNode.connect(context.destination);
sourceNode.buffer = buffer;
sourceNode.start(0, offset);
startedAt = context.currentTime - offset;
pausedAt = 0;
playing = true;
};
var pause = function() {
var elapsed = context.currentTime - startedAt;
stop();
pausedAt = elapsed;
};
var stop = function() {
if (sourceNode) {
sourceNode.disconnect();
sourceNode.stop(0);
sourceNode = null;
}
pausedAt = 0;
startedAt = 0;
playing = false;
};
var getPlaying = function() {
return playing;
};
var getCurrentTime = function() {
if(pausedAt) {
return pausedAt;
}
if(startedAt) {
return context.currentTime - startedAt;
}
return 0;
};
var getDuration = function() {
return buffer.duration;
};
return {
getCurrentTime: getCurrentTime,
getDuration: getDuration,
getPlaying: getPlaying,
play: play,
pause: pause,
stop: stop
};
}
Old thread (and very useful! thanks!) , but maybe worth mentioning that in the example above instead of
function update() {
window.requestAnimationFrame(update);
info.innerHTML = sound.getCurrentTime().toFixed(1) + '/' + sound.getDuration().toFixed(1);
}
update();
it may be more precise and less resource intensive to use a createScriptProcessor
like explained in this post
const audioBuffer = await audioContext.decodeAudioData(response.data);
const chan = audioBuffer.numberOfChannels;
const scriptNode = audioContext.createScriptProcessor(4096, chan, chan);
scriptNode.connect(audioContext.destination);
scriptNode.onaudioprocess = (e) => {
// ---> audio loop <----
};
[Update]
Note: As of the August 29 2014 Web Audio API spec publication, this feature has been marked as deprecated, and was replaced by AudioWorklet (see AudioWorkletNode). https://developers.google.com/web/updates/2017/12/audio-worklet
I've come up with this little animation that uses images in an array, but it's sloppy. What I'd really like is to modularize it so that I can use multiple instances of the same function to animate different arrays depending on which key is pressed. Also, I'd like to get it so that the animation stops when the key is released, if possible. I realize that calling all of those variables globally is a big no-no, but I have no idea how to make it work otherwise! Last but not least, I'd like to figure out how to get that last bit of inline script over to the external file and have it still work correctly. Any help would be much appreciated! But please note, I'm a novice with JavaScript, so please try to be as specific/detailed as possible so I can learn from your wisdom! Thank you!
I've created a jsfiddle.
HTML:
<div id="wrapper">
<span id="jajo"><img id="my_img" src="jajo.png" /></span>
<script>element = document.getElementById("jajo"); </script>
</div><!--wrapper-->
JavaScript:
var i = 0;
var element;
var waveArray = ["wave0.png","wave1.png","wave2.png","wave3.png","wave4.png","wave5.png"];
var jumpArray = ["jump0.png","jump1.png","jump2.png","jump3.png","jump4.png","jump5.png"];
var foodArray = ["food0.png","food1.png","food2.png","food3.png","food4.png","food5.png"];
document.onkeydown = function(event) {
var keyPress = String.fromCharCode(event.keyCode);
if(keyPress == "W") { // if "w" is pressed, display wave animation
increment ();
document.onkeyup = function(event) { // if "w" is released, display default image
document.getElementById("jajo").innerHTML= "<img alt='Jajo' src='jajo.png'>";
}
} else if(keyPress == "A") { // if "a" is pressed, display jump animation
} else if(keyPress == "S") { // if "s" is pressed, display food animation
}
}
function increment (){
i++;
if(i > (waveArray.length - 1)){
i = 0;
}
setTimeout(animation,1000/30);
}
function animation() {
var img = '<img src="' + waveArray[i] + '" alt="Jajo" />';
element.innerHTML = img;
setTimeout(increment, 2000/30);
}
Demo
http://jsfiddle.net/ghQwF/4/
Module
var animation = (function() {
var delay = 1000 / 30;
var map = {}, active = [], timer;
function animate() {
for(var i=0, l=active.length; i<l; ++i) {
var data = map[active[i]];
++data.index;
data.index %= data.array.length;
data.image.src = data.array[data.index];
}
timer = setTimeout(animate, delay);
}
function begin(e) {
var key = String.fromCharCode(e.keyCode),
data = map[key];
if(!data) return;
if(!active.length) timer = setTimeout(animate, delay);
if(!~active.indexOf(key)) active.push(key);
}
function end(e) {
var key = String.fromCharCode(e.keyCode),
data = map[key];
if(!data) return;
data.image.src = data.default;
var index = active.indexOf(key);
if(!~index) return;
active.splice(index, 1);
if(!active.length) clearTimeout(timer);
}
return {
add: function(data) {
data.index = data.index || 0;
data.image = data.image || data.target.getElementsByTagName('img')[0];
data.default = data.default || data.image.src;
map[data.key] = data;
},
remove: function(key) {
delete map[key];
},
enable: function() {
document.addEventListener('keydown', begin, false);
document.addEventListener('keyup', end, false);
},
disable: function() {
document.removeEventListener('keydown', begin, false);
document.removeEventListener('keyup', end, false);
clearTimeout(timer);
active = [];
}
};
})();
Example
animation.enable();
animation.add({
key: 'W',
target: document.getElementById('jajo'),
array: [
"http://static.spoonful.com/sites/default/files/styles/square_128x128/public/crafts/fingerprint-flowers-spring-craft-photo-420x420-aformaro-01.jpg",
"http://static.spoonful.com/sites/default/files/styles/square_128x128/public/crafts/paper-flowers-spring-craft-photo-420x420-aformaro-11.jpg",
"http://static.spoonful.com/sites/default/files/styles/square_128x128/public/crafts/tissue-paper-flowers-kaboose-craft-photo-350-fs-IMG_8971_rdax_65.jpg"
]
});
animation.add({ /* ... */ });
Methods
add
Adds a new animation, with parameters:
key: key to activate animation
target: wrapper of the image (optional if image is specified)
image: image to animate (optional, defaults to first image in target)
default: default url of the image (optional, defaults to original url)
index: initial position in array (optional, defaults to 0)
array: array of images
remove
Removes the animation related with key specified.
disable
Disables animations (typing keys would have no effect), and stops current ones.
enable
Enable animations (typing keys can start animations).