Play an audio file in Javascript - javascript

I'm trying to play an audio file in js. what am i doing wrong?
The script auto-stops after some events which is working 100%, now all I want to do is add a sound after it stops
Here is the code in:
function stop() {
stop_flag = true;
var audio = new Audio('play.mp3');
audio.play();
}
Here is the complete script.js the function is at the bottom.
chrome.runtime.onMessage.addListener(function(message) {
if (message.action == "init") {
init(message.options.divider, message.options.delay, message.options.maxLosses);
}
if (message.action == "stop") {
stop();
}
});
var stop_flag;
function init(divider, delay, maxLosses) {
var $table = $('table.dice_table').last();
var lastBetNumber;
var lastBetValue;
var lastProfit;
var losses = 0;
stop_flag = false;
loop_start();
function loop_start() {
lastBetNumber = getLastBetNumber();
var betValue = (getCurrentBalance() / divider).toFixed(9);
//var betValue = (getCurrentBalance() / divider);
lastBetValue = betValue;
$("input.bet").val(betValue);
play();
setTimeout(loop, delay);
}
function loop() {
if (stop_flag)
return;
waitForTableChange()
.then(roll)
.catch(loop_start);
function roll() {
//only if the last bet has changed \/
lastBetNumber = getLastBetNumber();
lastProfit = $table.children('tbody').children('tr').first().children('td').last().text()[0];
changeBet();
if (losses >= maxLosses)
return;
play();
setTimeout(loop, delay);
}
}
function waitForTableChange() {
return new Promise(function(resolve, reject) {
var n = 0;
attempt();
function attempt() {
n++;
if (n >= 100) {
reject();
return;
}
if (getLastBetNumber() == lastBetNumber) {
setTimeout(attempt, 100);
return;
}
resolve();
}
});
}
function changeBet() {
var newBetValue;
if (lastProfit == "-") {
losses++;
newBetValue = lastBetValue * 2;
} else {
losses = 0;
newBetValue = (getCurrentBalance() / divider).toFixed(9);
}
lastBetValue = newBetValue;
$("input.bet").val(newBetValue);
}
function play() {
$('input.clDicePlay').first()[0].click();
}
function getLastBetNumber() {
return $table.children('tbody').children('tr').first().children('td').first().text();
}
function getCurrentBalance() {
return $('.dice_select .chosen-single span').text().split('- ')[1].split(' ')[0];
}
}
function stop() {
var audio = new Audio('play.mp3');
stop_flag = true;
audio.play();
}

The problem is you are setting audio within the function stop(), which makes it local to that function. Once a function has been executed, all local vars are destroyed. You need to use the global scope to keep the object alive.
For example:
// set 'audio' in the global scope
var audio = new Audio('/path/to/default.mp3');
// create a play / pause button
function playPause(e) {
// NOTE: audio is from the gloabl scope
if (this.textContent == 'Play') {
audio.play();
this.textContent = 'Pause';
} else {
audio.pause();
this.textContent = 'Play';
}
}
window.onload = function() {
var a = document.getElementById('func');
a.addEventListener('click',playPause,false);
}
<button id="func">Play</button>

Related

How to add delay between each iteration in Javascript For loop

I'm creating a Simon game for FreeCodeCamp and I'm trying to use a for loop to play an audio sound for each piece of the pattern.
The code I have now plays the audio all at once which won't work for the game. How can I change this code so that it will play each audio sound individually with a .5 second break in between?
Here's the function that plays the audio and adds visual effect
function playAudio() {
if (colorColor === "red") {
red.play();
$('#red').effect("highlight", { color: colorColor }, 500);
} else if (colorColor === "blue") {
blue.play();
$('#blue').effect("highlight", { color: colorColor }, 500);
} else if (colorColor === "green") {
green.play();
$('#green').effect("highlight", { color: colorColor }, 500);
} else if (colorColor === "yellow") {
yellow.play();
$('#yellow').effect("highlight", { color: colorColor }, 500);
}
}
This is the function where I believe the issue is.
function playPattern() {
for (var i = 0; i < pattern.length; i++) {
colorColor = pattern[i];
setTimeout(playAudio, 500);
}
setTimeout(random, 750);
}
And here's the random() function only because it is called within playPattern()
function random() {
randomColor = colors[Math.floor(Math.random() * colors.length)];
colorColor = randomColor;
colorColor = colorColor.slice(1);
pattern.push(colorColor);
count++;
if (count < 10) {
document.getElementById("count").innerHTML = "0" + count;
} else {
document.getElementById("count").innerHTML = count;
}
playAudio();
pickCount = 0;
userPick = [];
}
Thanks!
Assuming your audios are all the same length:
var audioLength = 3000; // length of audio in ms
for (var i = 0; i < pattern.length; i++) {
colorColor = pattern[i];
setTimeout(playAudio, (i * (audioLength + 500));
}
In your code where you are doing:
for (var i = 0; i < pattern.length; i++) {
colorColor = pattern[i];
setTimeout(playAudio, 500);
}
What happens is that you basically define to run playAudio in 500ms for all patterns. the for loop is processed immediately and 500ms later all audios are being played together.
What you want is to call the next timeout only when the playAudio function ends.
let i = 0;
function playAudio() {
i++;
colorColor = pattern[i];
if (colorColor === "red") {
red.play();
$('#red').effect("highlight", { color: colorColor }, 500);
} else if (colorColor === "blue") {
blue.play();
$('#blue').effect("highlight", { color: colorColor }, 500);
} else if (colorColor === "green") {
green.play();
$('#green').effect("highlight", { color: colorColor }, 500);
} else if (colorColor === "yellow") {
yellow.play();
$('#yellow').effect("highlight", { color: colorColor }, 500);
}
if (i < pattern.length) {
setTimeout(playAudio, 500);
}
}
function random() {
randomColor = colors[Math.floor(Math.random() * colors.length)];
colorColor = randomColor;
colorColor = colorColor.slice(1);
pattern.push(colorColor);
count++;
if (count < 10) {
document.getElementById("count").innerHTML = "0" + count;
} else {
document.getElementById("count").innerHTML = count;
}
playAudio();
pickCount = 0;
userPick = [];
}
setTimeout(random, 750);
I based my answer on this answer.
What I did was to preload audio files by creating an object with the titles as keys and with an object value which contains the files' source and if it's loaded already:
const notes = ['do', 're', 'mi', 'fa', 'sol', 'la', 'ti'];
const audioFiles = notes.reduce((obj, title) => {
obj[title] = {
src: `notes/${title}.wav`,
loaded: false,
};
return obj;
}, {});
// Preload files.
Object.entries(audioFiles).forEach(([title, details]) => {
const audio = new Audio();
audio.addEventListener('canplaythrough', () => {
details.loaded = true;
});
audio.src = details.src;
});
The playPlaylist function takes a playlist (array of titles) and an object containing the audio files:
const playAudio = function playAudio(player, src) {
player.src = src;
player.play();
};
const playPlaylist = function playPlaylist(playlist, files) {
// TODO: Check if files in playlist are already loaded.
// while (!filesLoaded) { ... }
const player = new Audio();
let audioIndex = 0;
player.addEventListener('ended', () => {
audioIndex++;
if (audioIndex >= playlist.length) {
return;
}
setTimeout(() => {
playAudio(player, files[playlist[audioIndex]].src);
}, 500);
});
playAudio(player, files[playlist[audioIndex]].src);
};
Then just provide the playlist and pass the audioFiles object.
// Play sample playlist.
const playlist = 'do,mi,fa,sol,do,re,la,fa,mi'.split(',');
playPlaylist(playlist, audioFiles);
Just change the files and file paths to test.
You need to consider how much the song lasts, and include that on your timeout, for example if red lasts 1 min, you need to set your next timeout to setTimeout(blue, 60*1000 + 500), something like this :
function playAudio(color) {
if (colorColor === "red") {
red.play();
$('#red').effect("highlight", { color: colorColor }, 500);
setTimeout(function () {
playAudio("blue");
},60*1000 + 500);
} else if (colorColor === "blue") {
blue.play();
$('#blue').effect("highlight", { color: colorColor }, 500);
setTimeout(function () {
playAudio("green");
},60*1000 + 500);
} else if (colorColor === "green") {
//.... ETC
You don't need a for, just some recursion

How to make annyang detect no sound for 3 seconds or longer, then start next audio?

I got a person's help to build this so far. But my goal is after the first audio file, the program would still listen what the user says until the user finishes talking. And then if the program doesn't detect anything for 3 seconds(or longer), it will play the next audio. This program will do this over and over until all audio files have played.
However, there's one more case. If the user is a 2 years old kid, which means the kid might need to spend 2 seconds or longer between 2 sentences. In this case, the annyang might think the user has finished their sentences and play next audio. This way the program would interrupt the user speech. How should I handle this?
That person who gave me ideas of using setInverval and create some date objects and then minus date objects to get a time greater than 3 seconds, and play next audio. But it's not working. Is my logic of code wrong or there's a better way?
Any help I would appreciate it. Thank you in advance.
<script>
audio = new Audio();
if (annyang)
{
annyang.addCallback('start', function() {console.log('started listening!');});
annyang.addCallback('soundstart', function(){onSoundDetected();});
function monitorSound()
{
if(monitorId && monitorId > 0) return;
var monitorId = window.setInterval(function(){trackSound() }, 1000);
}
var lastSound= new Date();
function trackSound()
{
var now = new Date();
if ((now - lastSound) > 3000)
{
playNextAudio();
return;
}
}
function stopListening()
{
var monitorId = 0;
window.clearInterval(monitorId);
annyang.removeCallback('soundstart', onSoundHeard);
annyang.addCallback('result', function() {playNextAudio(); });
}
function onSoundHeard()
{
lastSound = new Date();
console.log(lastSound);
}
function playNextAudio()
{
if(audioIndex === playList.length - 1)
{
console.log("Played all audios");
return; // we have played all audio
}
else
{
annyang.addCallback('result', function() {
audio.src = dir + playList[audioIndex++] + extention;
audio.load();
//audio.ended = audio.play();
//audio.ended = setTimeout(function(){audio.play();}, 1500);
setTimeout(function(){audio.play();}, 1000);
});
}
}
function playFirstAudio()
{
audio.src = dir + playList[audioIndex] + extention;
audio.load();
audio.ended = setTimeout(function(){audio.play();}, 1000);
console.log('First audio is playing');
}
function onSoundDetected()
{
console.log('sound was detected');
playFirstAudio();
monitorSound();
}
// Start from here
var playList = ["1_hello", "2_how_old", "3_what_did_you_make"];
var dir = "sound/";
var extention = ".wav";
var audioIndex = 0;
annyang.debug(true);
};
</script>
You have logic errors in your code. When stopping and starting timers, you need to refer to the global variables.
This code isn't going to stop your timer:
function stopListening()
{
var monitorId = 0;
window.clearInterval(monitorId);
}
Your code also only initiates your play loop. It never starts the listening timer.
You logic will also play the first song twice.
To demonstrate the control flow, I have mocked the audio and annyang objects. The audio mock simulates the playing of 3 ten second audio files. When the window loads, the first audio mock will play. After which the annyang and the listening timer will start.
To mock annyang's sound detect there is a "mock sound" button. If it is clicked it will extend sound detection for another 3 seconds. Once 3 seconds goes by annyang will be paused and the next audio is played.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Annyang Mock</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
var AudioMock = function () {
this.src = null;
this.start = null;
this.timer = 0;
var that = this;
this.load = function () {
$('#AudioState').html("Loaded " + that.src);
}
this.play = function () {
that.start = new Date();
$('#AudioState').html("Playing: " + this.src);
window.setTimeout(that.end, 10000);
that.timer = window.setInterval(that.mockPlayTime, 500);
}
this.mockPlayTime = function () {
var d = new Date();
var elapsed = (d - that.start) / 1000;
$('#AudioState').html("Playing: " + that.src + " " + elapsed + " secs");
}
this.endMockPlayTime = function () {
window.clearInterval(that.timer);
}
this.end = function () {
that.endMockPlayTime();
$('#AudioState').html("Ended: " + that.src);
if (that.ended) {
that.ended();
}
}
this.ended = null;
};
var annyangMock = function () {
this.callbacks = {};
this.paused = false;
};
annyangMock.prototype.addCallback = function (name, callback) {
this.callbacks[name] = callback;
}
annyangMock.prototype.removeCallback = function (name, callback) {
this.callbacks[name] = null;
}
annyangMock.prototype.pause = function () {
$('#AnnyangState').html("Annyang: pause()");
this.paused = true;
}
annyangMock.prototype.start = function () {
$('#AnnyangState').html("Annyang: start()");
this.paused = false;
}
annyangMock.prototype.invoke = function (name) {
if (!this.paused) {
$('#AnnyangState').html("called(" + name + ")");
var cb = this.callbacks[name];
if (cb) cb();
}
}
annyangMock.prototype.debug = function (flag) { };
var annyang = new annyangMock();
var annyangMock = function () {
var callbacks = {};
var _paused = false;
var pause = function () {
$('#AnnyangState').html("Annyang: pause()");
_paused = true;
}
function addCallback(name, callback) {
callbacks[name] = callback;
}
function removeCallback(name, callback) {
callbacks[name] = null;
}
function invoke(name) {
$('#AnnyangState').html("called(" + name + ")");
if (name == "start") {
_paused = false;
}
else {
if (!_paused) {
var cb = callbacks[name];
if (cb) cb();
}
}
}
}
</script>
<script>
audio = new AudioMock();
var monitorId = 0;
if (annyang) {
annyang.addCallback('start', function () { console.log('started listening!'); });
annyang.addCallback('soundstart', onSoundHeard);
function monitorSound() {
lastSound = new Date();
if (monitorId && monitorId > 0) return;
monitorId = window.setInterval(trackSound, 1000);
annyang.start();
}
var lastSound = new Date();
function onSoundHeard() {
lastSound = new Date();
//console.log(lastSound);
}
function trackSound() {
var now = new Date();
var elapsed = now - lastSound;
$('#AnnyangState').html("Listening: " + (elapsed / 1000) + " secs");
if ((now - lastSound) > 3000) {
stopListening();
playNextAudio();
return;
}
}
function stopListening() {
window.clearInterval(monitorId);
monitorId = 0;
annyang.pause();
}
function playNextAudio() {
if (audioIndex === playList.length - 1) {
console.log("Played all audios");
return; // we have played all audio
}
else {
audio.src = dir + playList[++audioIndex] + extention;
load();
setTimeout(function () { play(); }, 1000);
}
}
function load() {
$($('#playlist li')[audioIndex]).addClass("loading");
audio.load();
}
function play() {
audio.play();
$('#playlist li').removeClass("loading")
var li = $('#playlist li')[audioIndex];
$(li).addClass("playing");
}
function playFirstAudio() {
annyang.pause();
audio.src = dir + playList[audioIndex] + extention;
load();
audio.ended = function () {
$('#playlist li').removeClass("playing");
lastSound = new Date(); // set timestamp
monitorSound(); // poll sound detection
}
setTimeout(function () { play(); }, 1000);
//console.log('First audio is playing');
}
// Start from here
var playList = ["1_hello", "2_how_old", "3_what_did_you_make"];
var dir = "sound/";
var extention = ".wav";
var audioIndex = 0;
annyang.debug(true);
$(document).ready(function () {
playFirstAudio();
var l = $("<ol>");
playList.forEach(function (j) {
l.append($("<li>").html(j));
});
$('#playlist').append(l);
})
};
</script>
<style type="text/css">
#playlist li {
width: 200px;
padding: 5px;
}
div {
padding: 15px;
}
#playlist li.playing {
border: 1px solid green;
background: #dedede;
}
#playlist li.loading {
border: 1px solid red;
background: #dedede;
}
</style>
</head>
<body>
<div>
<b>Annyang State:</b> <span id="AnnyangState"></span>
</div>
<div><b>Audio State:</b> <span id="AudioState"></span></div>
<div id="playlist">
<b>Playlist:</b>
</div>
<div id="Controls">
<input id="MockSound" type="button" value="Mock Sound" onclick="annyang.invoke('soundstart');" />
</div>
</body>
</html>

cordova media plugin status is not working

I want to know when the audio is still playing , and when it stopped to perform other actions . None of these options works for me. I tried for reproduce a same file, but only if is stopped, I try for a few days, but it still not work
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, function () {
$scope.media2.play();
}, function (b) {
}, function (a) {
alert(JSON.stringify(a));
});
}
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, bien,mal,status);
function bien(){ $scope.media2.play();}
function mal(){}
function status(estatus){ console.log("status")}
}
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, bien,mal,status);
function bien(){ $scope.media2.play();}
function mal(){}
function status(estatus){ alert(status)}
}
function reproducirAudio(ruta) {
var ruta = "/android_asset/www/sounds/button-1.mp3";
$scope.media2 = new Media(ruta, bien,mal,getStatusMessage);
function bien(){ $scope.media2.play();}
function mal(){}
function getStatusMessage(status){
if(status === 0){console.log('Media.MEDIA_NONE');}
else if(status === 1){console.log('Media.MEDIA_STARTING');}
else if(status === 2){console.log('Media.MEDIA_RUNNING');}
else if(status === 3){console.log('Media.MEDIA_PAUSED');}
else if(status === 4){console.log('Media.MEDIA_STOPPED');}
}
}
I have done this by creating a blank media player, then later setting the media. To test if it is playing just perform a check that tests if your media player is null or undefined.
$scope.media;
$scope.media = new Media("some/media/path.mp3);
if ($scope.media != null) {
someAction();
}
else {
otherAction();
}
Another option is to test if the media length is greater than 0.
$scope.media.getCurrentPosition(
//success
function (position) {
if (position > 0) {
console.log("A song is playing!");
}
},
// error
function () {
console.log("An error occured!");
}
);
finally it worked . I put this code and i realize a downgrade to cordova-media-plugin to 1.0.1 version
function reproducirAudioPorBoton(ruta) {
var audiofile = new Media('file:///android_asset/www/sounds/M.mp3');
audiofile.play();
var counter=0;
var timerDur = setInterval(function() {
counter = counter + 100;
if (counter > 2000) {
clearInterval(timerDur);
}
var dur = audiofile.getDuration();
if (dur > 0) {
clearInterval(timerDur);
console.log(dur + " sec");
}
}, 100);
}

Extend timeout function when button is clicked

This piece of code works, but I am trying to get the timeout function to reset to '0' every time the button is clicked.
var running = false,
count = 0,
run_for = 700;
var end_counter = function() {
if (running) {
running = false;
$("#status").text("Not Running");
alert(count);
started_at = 0;
}
};
$('button').click(function() {
if (running) {
count++;
} else {
running = true;
$("#status").text("Running");
count = 1;
setTimeout(end_counter, run_for);
}
});
Just cancel and restart it:
var timerId,
count = 0;
function end_counter() {
$("#status").text("Not Running");
alert(count);
count = 0;
}
$('button').click(function() {
$("#status").text("Running");
count++;
clearTimeout(timerId);
timerId = setTimeout(end_counter, 700);
});
var running = false,
count = 0,
run_for = 700;
var timer;
var end_counter = function() {
if (running) {
running = false;
$("#status").text("Not Running");
alert(count);
started_at = 0;
}
};
$('button').click(function() {
clearTimeout(timer);
if (running) {
count++;
timer = setTimeout(end_counter, run_for);
} else {
running = true;
$("#status").text("Running");
count = 1;
timer = setTimeout(end_counter, run_for);
}
});
Set timer to a variable - then clear it on click and restart it after count.

Jquery code works, javascript does not, what am i doing wrong?

Below is some code that has no jquery, but it is not working:
function start() {
var img = 0,
pic = ['nature', 'grass', 'earth', 'fall2', 'waterfall'],
slider = document.getElementsByClassName('slide_img'),
timerID = 0,
delay = 4000;
function back() {
img--;
if (img <= 0) {
img = pic.length - 1;
}
slider.src = 'pictures/' + pic[img] + '.jpg';
}
function go() {
img++;
if (img >= pic.length) {
img = 0;
}
slider.src = 'pictures/' + pic[img] + '.jpg';
}
document.getElementById('back').onclick = function() {
back();
}
document.getElementById('go').onclick = function() {
go();
}
slider.onmouseenter = function() {
clearTimeout(timerID);
}
document.getElementById('go').onmouseenter = function() {
clearTimeout(timerID);
}
document.getElementById('back').onmouseenter = function() {
clearTimeout(timerID);
}
slider.onmouseleave = function() {
clearTimeout(timerID);
auto();
}
function auto() {
timerID = setTimeout(function () {
go();
auto();
}, delay);
}
auto();
}
Here is what it is in jquery, this one works:
$('document').ready(function() {
var img = 0,
pic = ['nature', 'grass', 'earth', 'fall2', 'waterfall'],
slider = $('img.slide_img'),
timerID = 0,
delay = 4000;
function back() {
img--;
if (img <= 0) {
img = pic.length - 1;
}
slider.attr('src', 'pictures/' + pic[img] + '.jpg');
}
function go() {
img++;
if (img >= pic.length) {
img = 0;
}
slider.attr('src', 'pictures/' + pic[img] + '.jpg');
}
$('button#back').on('click', function() {
back();
});
$('button#go').on('click', function() {
go();
});
$(slider).on('mouseenter', function () {
clearTimeout(timerID);
});
$('button#go').on('mouseenter', function () {
clearTimeout(timerID);
});
$('button#back').on('mouseenter', function () {
clearTimeout(timerID);
});
$(slider).on('mouseleave', function () {
clearTimeout(timerID);
auto();
});
function auto() {
timerID = setTimeout(function () {
go();
auto();
}, delay);
}
auto();
});
What am i doing wrong, why is the first one not working. Im am trying to get rid of jquery so i dont have to include the source file.
You are not executing onload
try
var img = 0,
pic = ['nature', 'grass', 'earth', 'fall2', 'waterfall'],
slider,
timerID = 0,
delay = 4000;
window.onload=function() {
slider = document.getElementsByClassName('slide_img');
document.getElementById('back').onclick = function() {
back();
}
document.getElementById('go').onclick = function() {
go();
}
slider.onmouseover = function() {
clearTimeout(timerID);
}
document.getElementById('go').onmouseover = function() {
clearTimeout(timerID);
}
document.getElementById('back').onmouseover = function() {
clearTimeout(timerID);
}
slider.onmouseout = function() {
clearTimeout(timerID);
auto();
}
auto();
}
function auto() {
timerID = setTimeout(function () {
go();
auto();
}, delay);
}
function back() {
img--;
if (img <= 0) {
img = pic.length - 1;
}
slider.src = 'pictures/' + pic[img] + '.jpg';
}
function go() {
img++;
if (img >= pic.length) {
img = 0;
}
slider.src = 'pictures/' + pic[img] + '.jpg';
}
Use "onmouseover" and onmouseout" for canonical events:
slider.onmouseover = function() {
clearTimeout(timerID);
}
document.getElementById('go').onmouseover = function() {
clearTimeout(timerID);
}
document.getElementById('back').onmouseover = function() {
clearTimeout(timerID);
}
slider.onmouseout = function() {
clearTimeout(timerID);
auto();
}
And don't forget to define "slider" (the initial part is missing in your script):
var slider = document.querySelector('img.slide_img');
<body onload="start"></body>
The JavaScript start doesn't do anything. If you want to call a function, then you need to follow it with ().
This is a rather obtrusive way to deal with event handler assignment though. It is generally preferred to assign such things with JavaScript:
addEventListener('load', start);
See MDN for compatibility notes.
However, you don't have anything in your code that depends on the entire document being loaded. So you can simply:
<script>
start();
</script>
</body>
at the end of your document.
(You can put the whole script there for that matter, and keep it in an external file).

Categories