The code below randomly selects and plays audio elements (of different pitches, chosen by the user). I am working on a fade option; the user specifies a fade-time in seconds, which is passed in by ID (fade.value).
I am actually having two problems:
A) In the first iteration, the audio elements begins fading before it starts playing.
B) When the same element is called repeatedly, if the fade time is longer than the repeat time, the volume does not reset properly (which was the intended point of tone.volume=1), but "stays down."
function pickAndPlay(pitchSet, saturation){
var pitchCheck = rand(1,100);
if (pitchCheck <= saturation){
fyshuffle (pitchSet); // the Fischer-Yates shuffle
var tone = document.getElementById(pitchSet[0]);
tone.currentTime = 0;
tone.volume = 1;
tone.play();
if(fadeTime.value>0){
$(tone).animate({volume: 0}, fadeTime.value*1000);
};
document.getElementById("noteName").value=pitchSet[0];
console.log(iteration);
iteration++;
console.log("tone.volume:"+tone.volume);
};
};
function nextThing(millis,pitchSet,saturation){
if(iteration==0){pickAndPlay(pitchSet,saturation)};
return setInterval(pickAndPlay,millis,pitchSet,saturation); // this needs `return`: https://stackoverflow.com/questions/44609995/settimeout-recursion-javascript
};
Thanks for any suggestions or references you can offer about how to fix these problems.
For the second question, the animation is still running when the audio begins playing again. Just use the jQuery stop method:
function pickAndPlay(pitchSet, saturation){
var pitchCheck = rand(1,100);
if (pitchCheck <= saturation){
fyshuffle (pitchSet);
var tone = document.getElementById(pitchSet[0]);
tone.currentTime = 0;
$(tone).stop();
tone.volume = 1;
tone.play();
if(fadeTime.value>0){
$(tone).animate({volume: 0}, fadeTime.value*1000); console.log(fadeTime.value);
};
document.getElementById("noteName").value=pitchSet[0];
iteration++;
};
};
Related
I'm trying to loop through an array of pictures, but the code it's only displaying 1 of the pictures of the array.
Also, to be more confusing, it's not even displaying the element 0 of the array, but the element 1.
I've been reading about setTimeOut and other methods, but I don't really get the logic yet (javascript newbie).
My final goal it's to display a series of pictures for a specific amount of time (like photograms on a video) when the the mouse is over the element selected.
here's my code:
let aboutMePics = ["url('./img/aboutMeStill01.png')", "url('./img/aboutMeStill02.png')"]
const aboutMe = document.getElementById("aboutMe")
aboutMe.addEventListener("mouseover", function(){
for (let i = 0; i < aboutMePics.length; i++) {
document.body.style.background = aboutMePics[i];
}
})
aboutMe.addEventListener("mouseout", function(){
document.body.style.background = 'initial';
})
// IIFE just for a reduced scope
(function() {
const aboutMePics = ["url('./img/aboutMeStill01.png')", "url('./img/aboutMeStill02.png')"];
const aboutMe = document.getElementById("aboutMe");
let timeout;
function adjustPicture(index) {
document.body.style.background = aboutMePics[index];
timeout = setTimeout(() => adjustPicture(++index % aboutMePics.length), 3000);
}
aboutMe.addEventListener('mouseenter', function(){
adjustPicture(0);
});
aboutMe.addEventListener("mouseleave", function() {
clearTimeout(timeout);
document.body.style.background = 'initial';
});
})();
When the mouse enters the element, adjust the picture to pic 0.
After adjusting the picture, start a timeout for the next picture for 3 seconds
Modulus the index by the number of pictures so the index loops
When the mouse leaves the element, clear the timeout and reset the background
I'm trying (without much success) to create an array which contains slides being loaded into an iframe. One of these frames (/Events.php) uses PHP to query a WordPress database and show 1 post chosen at random. This slide needs to show a different random post every time the array loops through.
My code at them moment is...
<script type="text/javascript">
var frames = Array(
'http://www.example.com/Slide01.php', 5,
'http://www.example.com/Slide02.php', 5,
getRandomUrl(), 5,
'http://www.example.com/Slide04.php', 5
);
var i = 0, len = frames.length;
function getRandomUrl()
{
return "http://www.example.com/Events.php?=" + (new Date().getTime());
}
function ChangeSrc()
{
if (i >= len) { i = 0; } // start over
document.getElementById('myiframe').src = frames[i++];
setTimeout('ChangeSrc()', (frames[i++]*1000));
}
window.onload = ChangeSrc;
</script>
The only trouble is everytime /Events.php is shown it has the same number appended to it so therefore shows the same post in each loop.
I need to append a different number to the /Events.php slide on each loop so it generates different content each time.
I'm starting to think I'm approaching this in totally the wrong way so any help or pointers in the right direction would be appreciated!
Cheers,
Mark.
The issue is you are only calling getRandomUrl() once which is when you defined your array, this means the value will always be the same as its only returned once.
One solution would be to store the function itself in your array like so:
var frames = Array(
'http://www.example.com/Slide01.php', 5,
'http://www.example.com/Slide02.php', 5,
getRandomUrl, 5,
'http://www.example.com/Slide04.php', 5
);
And then call it in ChangeSrc() if its a function
function ChangeSrc()
{
if (i >= len) { i = 0; } // start over
var frame = frames[i++],
isFnc = typeof(frame) == "function";
if(isFnc){
frame = frame();
}
document.getElementById('myiframe').src = frame;
setTimeout(function(){
ChangeSrc()
}, frames[i++]*1000);
}
http://jsfiddle.net/redgg6pq/
A tip would be that you are only calling 'getRandomUrl' once, hence why it's always the same image. You want to call it each time you are in the loop.
I would suggest removing it from the static array, and calling it in the loop - does that make sense? :)
HTH
So, here's my issue.
I need to write a script to be run in the console (or via Greasemonkey) to automate clicking of certain links to check their output.
Each time one of these links is clicked, they essentially generate an image in a flash container to the left. The goal here is to be able to automate this so that the QC technicians do not have to click each of these thumbnails themselves.
Needless to say, there needs to be a delay between each "click" event and the next so that the user can view the large image and make sure it is okay.
Here is my script thus far:
function pausecomp(ms) {
ms = ms + new Date().getTime();
while (new Date() < ms){}
}
var itemlist, totalnumber, i;
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""));
for(i = 0; i < totalnumber; i = i + 1) {
console.log(i);
itemlist[i].childNodes[1].click();
pausecomp(3000);
}
Now, totalnumber gets me the total number of thumbnails, obviously, and then itemlist is a list of get-able elements so I can access the link itself.
If I run itemlist[0].childNodes[1].click() it works just fine. Same with 1, 2, 3, etc. However, in the loop, it does nothing and it simply crashes both Firefox and IE. I don't need cross-browser capability, but I'm confused.
There is a built-in JS function "setInterval(afunction, interval)" that keeps executing a given function every "interval" miliseconds (1000 = 1s).
This fiddle shows how to use setTimeout to work through an array. Here is the code:
var my_array = ["a", "b", "c", "d"];
function step(index) {
console.log("value of my_array at " + index + ":", my_array[index]);
if (index < my_array.length - 1)
setTimeout(step, 3000, index + 1);
}
setTimeout(step, 3000, 0);
Every 3 seconds, you'll see on the console something like:
value of my_array at x: v
where x is the index in the array and v is the corresponding value.
The problem with your code is that your pausecomp loop is a form of busy waiting. Let's suppose you have 10 items to go through. Your code will click an item, spin for 3 seconds, click an item, spin for 3 seconds, etc. All your clicks are doing is queuing events to be dispatched. However, these events are not dispatched until your code finishes executing. It finishes executing after all the clicks are queued and (roughly) 30 seconds (in this hypothetical scenario) have elapsed. If the number of elements is greater that's even worse.
Using setTimeout like above allows the JavaScript virtual machine to regain control and allows dispatching events. The documentation on setTimeout is available here.
People were correct with SetInterval.
For the record, here's the completed code:
/*global console, document, clearInterval, setInterval*/
var itemlist, totalnumber, i, counter;
i = 0;
function findmepeterpan() {
"use strict";
console.log("Currently viewing " + (i + 1));
itemlist[i].scrollIntoView(true);
document.getElementById("headline").scrollIntoView(true);
itemlist[i].style.borderColor = "red";
itemlist[i].style.borderWidth = "thick";
itemlist[i].childNodes[1].click();
i = i + 1;
if (i === totalnumber) {
clearInterval(counter);
console.log("And we're done! Hope you enjoyed it!");
}
}
function keepitup() {
"use strict";
if (i !== 0) {
itemlist[i - 1].style.borderColor = "transparent";
itemlist[i - 1].style.borderWidth = "medium";
}
findmepeterpan();
}
itemlist = document.getElementsByClassName("image");
totalnumber = parseInt(document.getElementById("quickNavImage").childNodes[3].firstChild.firstChild.nodeValue.replace(/[0-9]* of /, ""), 10);
counter = setInterval(keepitup, 1500);
I'm looping through a set of images within an array of animation frames. There are 7 images, and looping from 1-7 completes the animation. I need this animation to loop indefinitely, but I was wondering which of these is the best approach:
Loop by modifying array
/* Pull image from start of array. */
var image = frames.shift();
/* Process image. */
...
/* Add image back to end of array. */
frames.push(image );
Loop using counter variable
/* Pull image by counter offset. */
var image = frames[counter];
/* Process image. */
...
/* Increment or reset counter value. */
counter + 1 === frames.length ? counter = 0 : counter = counter + 1;
Is there a reason I'd chose one over the other? Alternatively, is there a better approach to this?
Modifying the array is going to be more expensive than simply using a variable to keep track of your position in the array. The better way to do this, if you're looping indefinitely, seems to just be to use a while loop (rather than using a for loop where you reset the counter inside):
var i = 0;
while (true) {
doSomething to array[i];
i = (i+1) % array.length;
}
However if your goal really is having an animation proceed indefinitely every time a given interval elapses, a loop isn't ideal at all. Use setInterval instead.
var frames = ...; //your images
var i = 0;
function animate() {
do something to frames[i];
i = (i+1) % array.length;
}
setInterval(animate, time_between_runs);
where time_between_runs is how much time should elapse before the function is called again.
Alternatively a circular linked list also can be used I think. To turn an array of objects into a circular linked list:
frames.forEach(function(elem, index) {
elem.next = frames[index + 1] || frames[0];
});
And now you can do something like this:
setInterval(function() {
frame = frame.next;
....
}, delay);
One possibility is to ditch the array and use a linked list.
Make each frame an object that points to the next object. The last one then points to the first. Then all you need to do is reference the next object.
var curr = first; // reference to first frame object
setInterval(function() {
// process image
curr.image.doSomething();
// proceed to next
curr = curr.next;
}, 1000);
No counters to mess with this way.
Setting up the linked list is usually pretty simple, and can likely be done with just a little modification to the current code that's setting up the Array.
var first = new Frame(); // This is your entry point
var current = first; // This holds the current frame during setup
for (var i = 0; i < totalFrames; i++) {
current.next = new Frame(); // Make the current reference a new frame
current = current.next; // Make the new frame current
}
current.next = first; // circular reference back to the first.
function Frame() {
// set up this frame
}
Then first is your starting point.
Or the linking could be done within the Frame constructor.
var first = new Frame(null);
var current = first;
for (var i = 0; i < totalFrames; i++) {
current = new Frame(current);
}
current.next = first;
function Frame(currFrame) {
// link this frame to the given one
if (currFrame)
currFrame.next = this;
// set up the rest of this frame
}
I am building a custom audio player (planning phase for now :) ), and I am stuck with this. I wanna calculate the total duration of all my songs in the beginning. I know that I need to load each song in order to know its duration. If I set the src attribute for instance for the first song I get the duration of it with the loadedmetadata event handler. But how to get the sum of them all? Do I need to load each one individually? Do I need to set the src attr of the audio element for each song I have in the list? Any hints would be much appreciated!
I was here yesterday because I needed to solve this exact same problem and, of course, StackOverflow was top of the search results - even for this four year-old unanswered questions. Alas my searches for a native JS solution proved fruitless, though I tried several different suggestions from here and there, so here's what I eventually came up with myself...
function tracksTotalLength(Tracklist) {
var tempAudio = document.createElement('audio'),
L = Tracklist.length,
i = 0, // iterator
TTL = 0, // TotalTrackLength
flag = true;
// set media-type and mute (belt and braces)
tempAudio.type = 'audio/mpeg';
tempAudio.muted = true;
tempAudio.addEventListener(
'durationchange',
function(e) {
TTL += e.target.duration;
/* DEBUG ONLY */ console.log('TTL:',TTL);
recursiveUpdater(i++);
},
false
);
function recursiveUpdater(ii) {
if (!isNaN(TTL) && ii < L) {
tempAudio.src = Tracklist[ii];
// fires recursiveUpdater()
// via 'durationchange' event
} else {
// sometimes not all values are caught
// and TTL = NaN, so
// reset and have another run at it
if (isNaN(TTL) && flag) {
i = 0;
flag = false;
recursiveUpdater(i);
} else (
// but you can't recurse all day so
// remove tempAudio
tempAudio = null;
// if it hasn't caught return false
return (!isNaN(TTL) : TTL : false);
}
}
}
return recursiveUpdater(i);
}
var tracks = [
'path/to/track01.mp3',
'path/to/track02.mp3',
'path/to/track03.mp3',
/*...etc... */
];
var totalLength = tracksTotalLength(tracks);
//=> totalLength = seconds (float) or false
The function tracksTotalLength() returns the total time of the list in seconds which can then be rounded into hh:mm:ss as per this classic StackOverflow answer.
The 'durationchange' is fired when the metadata is loaded or changed. More on this at Media Events (MDN)
Tested in latest Firefox and Chromium browsers on Lubuntu 14.04 with tracklists of 5-50 items.
Hope this helps someone, or at least encourages a true JS guru to come up with a better routine.