Loop audio a limited number of times - javascript

I am trying to play music in my web browser under a certain condition, threshold = true for 10 iteration and here is my code:
<script>
var threshold = true;
for (var i=0 ; i<10; i++){// number of iteration
if ( threshold){
var audio = new Audio('song.mp3');
audio.play();
}
}
</script>
while I don't get any error the song won't be played in my browser; therefore I don't know what can possibly be wrong.

Firstly, you can't generally call audio.play() unless you're doing it in immediate response to some sort of user action. This is due to the autoplay policy.
Next, I would leave the looping up to the browser:
audio.loop = true;
Now, use the seeked event to figured out when the audio went back in time. (Most likely, it looped.) When this happens, increment a loop count. Then, see if we've looped too many times and pause if so.
let maxLoops = 10;
let lastTime = 0;
let loops = 0;
audio.addEventListener('seeked', (e) => {
if (e.target.currentTime < lastTime) {
loops++;
}
if (loops >= maxLoops) {
e.target.pause();
}
lastTime = e.target.currentTime;
});

Related

Why is this function initiating before it is called? [duplicate]

I need to perform a huge calculation. So, I refer here to create a simple loading screen. Unfortunately, the loading screen is shown after the calculation complete.
Here is my code
class RosterScheduler
{
.........................
autoAssign()
{
var startDate=parseInt($("#autoPlannStartDate").val());
var endDate=parseInt($("#autoPlanEndDate").val());
$("body").addClass("loading");
if (startDate>endDate)
alert("Invalid start date or end date selection");
else
{
if (this.rosterTable.haveInvalidPreferredShift(startDate,endDate))
alert("Invalid shift requirement detected");
else
{
var self=this;
var finalRoster;
var roster,tempAverageSD,lowestAverageSD=100.0;
this.theLowestSDRosters=[];
this.theLowestMissingShiftRosters=[];
for (var i=0;i<100;i++)
{
this._genRoster(startDate,endDate);
}
console.log("Done");
}
}
}
}
I found that if I remark the for loop, the loading screen working properly.
If I uncomment the for loop, the loading screen is shown after the "Done" is shown in the console. How can I fix the problem? I have tried to convert the function _genRoster into a Promise function, however, it does not work.
It looks like _genRoster is blocking the browser, and not giving it any resources to re-render/repaint until the loop is completed. One possibility would be to run the loop after giving the browser a few ms to render the .loading:
this.theLowestSDRosters = [];
this.theLowestMissingShiftRosters = [];
setTimeout(() => {
for (var i = 0; i < 100; i++) {
this._genRoster(startDate, endDate);
}
console.log("Done");
}, 50);
It might be more elegant to call a function with 0 timeout after a single requestAnimationFrame, thus giving the browser time to repaint and then running the heavy loop immediately afterwards:
(warning: the following code will block your browser for a bit)
document.body.style.backgroundColor = 'green';
window.requestAnimationFrame(() => {
console.log('start, setting timeout');
setTimeout(() => {
for (let i = 0; i < 1000000000; i++) {
}
console.log('done');
});
});

Problem streaming video using media source extensions

I seem to have a very strange problem. I am trying to play a video which is being streamed live using a web browser. For this, I am looking at the MediaSource object. I have got it in a way so that the video is taken from a server in chunks to be played. The problem is that the first chunk plays correctly then playback stops.
To make this even more strange, if I put the computer to sleep after starting streaming, then wake it up andthe video will play as expected.
Some Notes:
I am currently using chrome.
I have tried both of them ,with and without calling MediaSource's endOfStream.
var VF = 'video/webm; codecs="vp8,opus"';
var FC = 0;
alert(MediaSource.isTypeSupported(VF));
var url = window.URL || window.webkitURL;
var VSRC = new MediaSource();
var VURL = URL.createObjectURL(VSRC);
var bgi, idx = 1;
var str, rec, dat = [], datb, brl;
var sbx;
//connect the mediasource to the <video> elment first.
vid2.src = VURL;
VSRC.addEventListener("sourceopen", function () {
// alert(VSRC.readyState);
//Setup the source only once.
if (VSRC.sourceBuffers.length == 0) {
var sb = VSRC.addSourceBuffer(VF);
sb.mode = 'sequence';
sb.addEventListener("updateend", function () {
VSRC.endOfStream();
});
sbx = sb;
}
});
//This function will be called each time we get more chunks from the stream.
dataavailable = function (e) {
//video is appended to the sourcebuffer, but does not play in video element
//Unless the computer is put to sleep then awaken!?
sbx.appendBuffer(e.result);
FC += 1;
//These checks behave as expected.
len.innerHTML = "" + sbx.buffered.length + "|" + VSRC.duration;
CTS.innerHTML = FC;
};
You are making two big mistakes:
You can only call sbx.appendBuffer when sbx.updating property is false, otherwise appendBuffer will fail. So what you need to do in reality is have a queue of chunks, and add a chunk to the queue if sbx.updating is true:
if (sbx.updating || segmentsQueue.length > 0)
segmentsQueue.push(e.result);
else
sbx.appendBuffer(e.result);
Your code explicitly says to stop playing after the very first chunk:
sb.addEventListener("updateend", function () {
VSRC.endOfStream();
});
Here is what you really need to do:
sb.addEventListener("updateend", function () {
if (!sbx.updating && segmentsQueue.length > 0) {
sbx.appendBuffer(segmentsQueue.shift());
}
});

Create an loading screen for long calculation

I need to perform a huge calculation. So, I refer here to create a simple loading screen. Unfortunately, the loading screen is shown after the calculation complete.
Here is my code
class RosterScheduler
{
.........................
autoAssign()
{
var startDate=parseInt($("#autoPlannStartDate").val());
var endDate=parseInt($("#autoPlanEndDate").val());
$("body").addClass("loading");
if (startDate>endDate)
alert("Invalid start date or end date selection");
else
{
if (this.rosterTable.haveInvalidPreferredShift(startDate,endDate))
alert("Invalid shift requirement detected");
else
{
var self=this;
var finalRoster;
var roster,tempAverageSD,lowestAverageSD=100.0;
this.theLowestSDRosters=[];
this.theLowestMissingShiftRosters=[];
for (var i=0;i<100;i++)
{
this._genRoster(startDate,endDate);
}
console.log("Done");
}
}
}
}
I found that if I remark the for loop, the loading screen working properly.
If I uncomment the for loop, the loading screen is shown after the "Done" is shown in the console. How can I fix the problem? I have tried to convert the function _genRoster into a Promise function, however, it does not work.
It looks like _genRoster is blocking the browser, and not giving it any resources to re-render/repaint until the loop is completed. One possibility would be to run the loop after giving the browser a few ms to render the .loading:
this.theLowestSDRosters = [];
this.theLowestMissingShiftRosters = [];
setTimeout(() => {
for (var i = 0; i < 100; i++) {
this._genRoster(startDate, endDate);
}
console.log("Done");
}, 50);
It might be more elegant to call a function with 0 timeout after a single requestAnimationFrame, thus giving the browser time to repaint and then running the heavy loop immediately afterwards:
(warning: the following code will block your browser for a bit)
document.body.style.backgroundColor = 'green';
window.requestAnimationFrame(() => {
console.log('start, setting timeout');
setTimeout(() => {
for (let i = 0; i < 1000000000; i++) {
}
console.log('done');
});
});

titanium start and stop sound

I have this working in Javascript but can't seem to get it working on Titanium.
Here is the code:
var index = 0;
var i = 0;
// Filename
var wordSoundArray = [];
wordSoundArray.push('audio/the.mp3');
wordSoundArray.push('audio/of.mp3');
wordSoundArray.push('audio/and.mp3');
wordSoundArray.push('audio/a.mp3');
wordSoundArray.push('audio/to.mp3');
wordSoundArray.push('audio/in.mp3');
wordSoundArray.push('audio/is.mp3');
wordSoundArray.push('audio/you.mp3');
wordSoundArray.push('audio/that.mp3');
wordSoundArray.push('audio/it.mp3');
wordSoundArray.push('audio/he.mp3');
wordSoundArray.push('audio/was.mp3');
wordSoundArray.push('audio/for.mp3');
wordSoundArray.push('audio/on.mp3');
wordSoundArray.push('audio/are.mp3');
newWordBtn.addEventListener("click", function(e){
wordLabel.text = newWordArray[i++];
if (i === newWordArray.length)
i = 0;
var snd = Titanium.Media.createSound({url:wordSoundArray[index++]});
if (index === wordSoundArray.length)
index = 0;
if (snd.isPlaying()) {
snd.stop();
snd.play();
} else {
snd.play();
}
});
When a user presses the button they get a new word and the sound that goes with that word. However, if the user presses the button before the sound is finished it simply starts the new sound and they overlap each other. That is where the snd.isPlaying portion of the code of the code comes in. I'm pretty sure my mistake is in there.
So you actually have dead code here:
var snd = Titanium.Media.createSound({url:wordSoundArray[index++]}));
...
// You just created the sound, so it will never be playing right off the bat
if (snd.isPlaying()) {
// This will never be called
snd.stop();
snd.play();
} else {
// This will happen every time the user clicks the button
snd.play();
}
I think its good practice to pre-load all your sound assets before you start execution, so maybe try replacing your wordSoundArray with entries of the form:
wordSoundArray.push(Titanium.Media.createSound({url:'audio/the.mp3'});
Once you have done this (all our sound assets are preloaded, this will be good for memory too) we can change the listener to something like this:
newWordBtn.addEventListener("click", function(e){
wordLabel.text = newWordArray[i++];
if (i === newWordArray.length)
i = 0;
// Instead of creating the sound, just fetch it!
var snd = wordSoundArray[index++];
if (index === wordSoundArray.length)
index = 0;
// Now this will work, but maybe you want to make sure all the sounds are off instead?
if (snd.isPlaying()) {
snd.stop();
snd.play();
} else {
snd.play();
}
});
Looking at your code though, it appears you want to stop the previous sound playing and then start the next one, so you would need to change the listener to this:
newWordBtn.addEventListener("click", function(e){
wordLabel.text = newWordArray[i++];
if (i === newWordArray.length)
i = 0;
// Stop the last sound from playing
if(index > 0) {
var lastSound = wordSoundArray[index-1];
lastSound.stop();
}
// Instead of creating the sound, just fetch it!
var nextSound = wordSoundArray[index++];
if (index === wordSoundArray.length)
index = 0;
// Play the next sound
nextSound.play();
});

condition and loop: how can i don't resume the music when calling the function several times?

i have this function to play music with web audio API:
function playMusic(){
if(countPre<count ){
audio0.play();
audio0.src = '0.mp3';
audio0.controls = true;
audio0.autoplay = true;
audio0.loop = true;
source = context.createBufferSource();
source.connect(analyser);
analyser.connect(context.destination);
}
else{audio0.pause();}
}
However, the value of count and countPre are generated in a loop that runs 10 times per second.
I have to put the function playMusic inside that loop in order to update the values.
And here comes the problem:
I call playMusic 10 times per second! Every time, the music resumes.
I don't want it resumes, i want it plays continuously as long as the play condition is matched.
So is there any solution?
You have to check that music is not already playing in your condition.
Don't think there's an audio method for that.
You can do it by many ways, example :
function playMusic(){
if(countPre<count && !this.is_playing ){
this.is_playing = true;
audio0.play();
audio0.src = '0.mp3';
audio0.controls = true;
audio0.autoplay = true;
audio0.loop = true;
source = context.createBufferSource();
source.connect(analyser);
analyser.connect(context.destination);
}
else{audio0.pause();
this.is_playing = false}
}

Categories