How to trigger a function which is inside another function - javascript

I have function running every second (for clock) .
I wanted to add sound to every tick tick....(second) but as function is running automatic (means no triggering is taking place) and when sound added produces an error
Error showing is :
Uncaught DOMException: play() failed because the user didn't interact with the document first.
Know that it is not possible to play audio automatic .
So tried to create a start sound btn which play the audio but as second time (of clock) and don't match with audio . So there is a mismatch
I am trying to add that function(audio play) inside second(clock time) calculator .
Is this possible or not
Link to similar question but not able to find the solution
The code is as follows
setInterval(clockRunner, 1000);
function clockRunner() {
//Clock code goes here
function trigger() {
var audioElement = document.getElementById('audiotag1');
audioElement.play();
}
}
<audio id="audiotag1" src="https://soundbible.com/mp3/Tick-DeepFrozenApps-397275646.mp3" preload="metadata" controls></audio>
<button onclick="trigger()">Start</button>
Update
Added event listener inside the outer function but this way sound is playing once not every second .
One solution here can be adding another setInterval but this will again create problem of playing sound at different timing of clock's second
Also it shows the same error in the console(mentioned above) until user clicks the btn
setInterval(clockRunner, 1000);
function clockRunner() {
//Clock code goes here
document.getElementById('demo').innerHTML = new Date();
setInterval(trigger, 1000);
document.getElementById("triggerSnd").addEventListener('click', trigger)
function trigger() {
var audioElement = document.getElementById('audiotag1');
audioElement.play();
}
}
<audio id="audiotag1" src="https://soundbible.com/mp3/Tick-DeepFrozenApps-397275646.mp3" preload="metadata" controls></audio>
<button id="triggerSnd">Start</button>
<div id="demo"></div>
Thanks for help in advance

If you make use of some variables, you can control your code to perform like a clock. It's worth noting that there are two possibilities, either the clock has started or it has stopped.
The interval will run when you clicked to start and it will be cleared in stopping it. Here I can't notice any mismatch between the sound and the timer.
var start = document.getElementById("triggerSnd")
var audioElement = document.getElementById('audiotag1')
var demo = document.getElementById('demo')
var interval, isMuted = true
// The second argument of addEventListener should be a function
// that receives an argument 'event'
clockRunner();
clock(); // call firstly
setInterval(() => clock(), 1000)
start.addEventListener('click', clockRunner)
function clock(isMuted){
//Clock code goes here
demo.innerText = new Date();
audioElement.play();
}
function clockRunner() {
audioElement.muted = isMuted
start.innerText = isMuted ? "Start" : "Stop"
isMuted = !isMuted
}
<audio id="audiotag1" src="https://soundbible.com/mp3/Tick-DeepFrozenApps-397275646.mp3" preload="metadata"></audio>
<button id="triggerSnd">Start</button>
<div id="demo"></div>

I've changed my answer completely. Now I didn't need to use an event listener, just the proper API of HTMLMediaElement that audio uses. Note that I've add loop attribute to tick's audio.
var blast = document.getElementById("blast");
var tick = document.getElementById("tick");
var tickDuration, timeDiff, lastTime
tick.onloadedmetadata = () => tickDuration = tick.duration
timeDiff = 0.3 // max difference between sync, dont lower it or it will bug (can explain if u want)
lastTime = 0
blast.addEventListener("timeupdate", () => {
let bct = blast.currentTime
let tct = tick.currentTime
let syncBlastTime = tickDuration - (tickDuration - bct%tickDuration)
let diff = Math.abs(syncBlastTime - tct)
lastTime = bct
console.log(`Blast: ${bct} Tick: ${tct}\nSynced time difference (seconds): ${diff.toFixed(6)}`)
if(diff > timeDiff) { // add this or it will bug the tick audio
tick.currentTime = syncBlastTime
}
})
blast.addEventListener("pause", () => {
tick.pause()
})
blast.addEventListener("playing", () => {
tick.play()
})
<audio id="blast" src="http://stream.arrowcaz.nl/caz64kmp3" controls="loop"></audio>
<audio id="tick" src="https://soundbible.com/mp3/A-Z_Vocalized-Mike_Koenig-197996831.mp3" loop controls='loop'></audio>
<br />

Related

Play random sound on button that is spammable

I have a problem that i want to have a button, that plays random sound after I press it, but the other thing is that i want to spam the button and so does the sound. What I mean is that i dont want to wait for the sound to finish to play it again and like have 5 sounds or more playing depending on your click speed.
for(var i=0;i<1;i++)
{
document.getElementById("MyAudio.mp3")[i].addEventListener("click",function(){});
}
function playAudio()
{
var audio = new Audio("MyAudio.mp3");
audio.play();
}
<button onlick="playAudio()"></button>
That sounds fun and there's many ways to do it. I think the easiest way would be to pre-load the songs ahead of time and randomize it.
// Your sounds
let sounds = [new Audio('https://download.samplelib.com/mp3/sample-3s.mp3'), new Audio('https://download.samplelib.com/mp3/sample-6s.mp3'), new Audio('https://www.soundjay.com/human/fart-01.mp3')];
let lastSound = 0; // Not necessary but we don't want overlapping tracks.
// Listen for click on button (ID soundmachine)
// pointerdown is more suitable so adjusted it.
document.querySelector("#soundmachine1").addEventListener("pointerdown", () => {
// This is a small fix to allow the sound to be spammable and clear last track.
sounds[lastSound].pause();
sounds[lastSound].currentTime = 0;
let random = Math.floor(Math.random() * sounds.length);
sounds[random].play();
lastSound = random; // For the next click we want to cancel this sound if it's playing.
console.log(lastSound); // Just so you can see it count/play
});
document.querySelector("#soundmachine2").addEventListener("pointerdown", () => {
// Play till completed
let random = Math.floor(Math.random() * sounds.length);
sounds[random].play();
});
<button id="soundmachine1">click me ver1</div>
<button id="soundmachine2">click me ver2</div>
Something like this!
There's some odd things going on with this code, so let's rethink it from the beginning. First of all, you want to create an onclick handler on your button that plays your mp3 file. Let's do that first:
let audio = new Audio("MyAudio.mp3");
document.querySelector("button").addEventListener("click", () => {
audio.play()
});
Now, it sounds like you want it to play from the beginning every time when clicked, and not wait till it completes (which is the default behavior). So we need to reset the playback every time you click.
let audio = new Audio("MyAudio.mp3");
document.querySelector("button").addEventListener("click", () => {
audio.currentTime = 0;
audio.play();
});
🎉 🎶
And your HTML can just look like this:
<button>Play</button>

Why audio error is showing up when it is played muted

I have a error which is
Error showing is :
Uncaught DOMException: play() failed because the user didn't interact with the document first.
The error does not prevent function to run but it should not be shown as some of the question in SO , Chrome , Mozilla documentation states that audio or video which are muted are not blocked from playing.
So there must be something I am doing wrong . Can you tell that or any way to remove the error.
I have muted the audio at first hand then why it is still showing up . As function is running every second so error is also showing up every second .
The error stop showing up when I click the start btn which plays the audio .
Is there any way to remove this error :
var start = document.getElementById("triggerSnd")
var audioElement = document.getElementById('audiotag1')
var demo = document.getElementById('demo')
var interval, isMuted = true
clockRunner();
clock();
setInterval(() => clock(), 1000)
start.addEventListener('click', clockRunner)
function clockRunner() {
audioElement.muted = isMuted
start.innerText = isMuted ? "Start" : "Stop"
isMuted = !isMuted
}
function clock(isMuted) {
//Clock code goes here , here only used a simple timer
demo.innerText = new Date();
audioElement.play();
}
<audio id="audiotag1" src="https://soundbible.com/mp3/Tick-DeepFrozenApps-397275646.mp3" preload="metadata"></audio>
<button id="triggerSnd">Start</button>
<div id="demo"></div>
It is not showing up in above snippet but can seen in console of VS code(Using it, so can tell about it) every second
Thanks for help in advance

How can I detect when a button is held in js?

I am trying to detect when a button is being held down by a finger for an unspecified amount of time. However I can't figure out how to do that.
I tried doing this like the sample online showed
const b = document.getElementById("hold-me");
b.onclick = function() {
setTimeout(alert, 2000, "you held me down for 2 second");
}
<body>
<button id="hold-me">Hold me</button>
</body>
However it doesn't correspond to what I want to do as I want to be able to have the event to fire as long as the button is pressed.
Edit:
To add some clarification, the set timeout function is here because it's roughly what the example I found online did. Here is the example: https://codepen.io/thetallweeks/pen/uAEGr?editors=0010
The example I found online didn't helped because I want to query if the button is press continuously to know wether I should a action of moving my rc car or not.
1'st variant, event fire after "unspecified amount of time";
const b = document.getElementById("hold-me");
let time;
b.onpointerdown = function() {
time = Date.now();
}
b.onpointerup = function() {
console.log(`you held me down for ${Date.now() - time} milliseconds`);
}
<body>
<button id="hold-me">Hold me</button>
</body>
2'nd variant, event fire during "unspecified amount of time":
const b = document.getElementById("hold-me");
let timer;
b.onpointerdown = function() {
timer = setTimeout(alert, 2000, "you held me down for 2 second");
}
b.onpointerup = function() {
clearTimeout(timer);
}
<body>
<button id="hold-me">Hold me</button>
</body>
Use the mousedown event and get the current time every time the button is pressed. Then, in the timer function, see if it's at least however many seconds the user has determined past that start time. When the button is released, reset the time.
Also, you should use the modern API for event binding of .addEventListener(), rather than setting event properties.
let start = null;
const elapsedTime = +prompt("How many seconds should we wait?");
const button = document.getElementById("hold-me");
button.textContent += " for at least " + elapsedTime + " seconds.";
button.addEventListener("mousedown", function() {
start = new Date(); // Record the start time
setTimeout(function(){
// If there is a start time and its now at least 2 seconds past that...
if(start && (new Date() > start.setSeconds(start.getSeconds() + elapsedTime))){
alert("You held me down for " + elapsedTime + " seconds.");
}
}, elapsedTime * 1000);
});
document.getElementById("hold-me").addEventListener("mouseup", function() {
start = null; // Cancel the time
});
<button id="hold-me">Hold me</button>
It depends upon how much time you consider that the user is holding. You can say, for example, that a time of less than 1 second is click and greater a button being hold down.
Furthermore, it's good to point that holding is the combination of two Javascript events: mouseup and mousedown. The first gets track of wheter the click pushes down the button and the second when user left the mouse click button.
let startTime, clickTime = 2000 // in milliseconds
$("#btn").mousedown(()=>{
startTime = new Date()
console.log("Holding")
// Your logic goes here (while holding)
})
$("#btn").mouseup(()=>{
var elapsedTime = new Date() - startTime
elapsedTime <= clickTime ? console.log("It's a click") : console.log(`It was held by ${elapsedTime} ms`)
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id='btn'>Hold! :)</button>
Finally: you are able to detect whether a button is being held down or not based on the time you set to be considered a button holding, then you can implement your logic.

How do I stop one audio track when another one starts? (with only a text link)

I was trying to play a list of audio files on the same web page with only a text link as play button, and I found the following solution, which works really perfect for me:
(thanks #linstantnoodles for this script!)
<audio id="song1" preload='auto'>
<source src='song1.mp3' type='audio/mp3' />
</audio>
<audio id="song2" preload='auto'>
<source src='song2.mp3' type='audio/mp3' />
</audio>
Listen
Listen
<script type="text/javascript">
// List of audio/control pairs
var playlist = [{
"audio": document.getElementById('song1'),
"control": document.getElementById('song1-control')
}, {
"audio": document.getElementById('song2'),
"control": document.getElementById('song2-control')
}];
for (var i = 0; i < playlist.length; i++) {
var obj = playlist[i];
var audio = obj.audio;
var control = obj.control;
// returns a closure
var listener = function (control, audio) {
return function () {
var pause = control.innerHTML === 'Pause';
control.innerHTML = pause ? 'Listen' : 'Pause';
// Update the Audio
var method = pause ? 'pause' : 'play';
audio[method]();
// Prevent Default Action
return false;
}
};
control.addEventListener("click", listener(control, audio));
}
</script>
My only problem with this code is, that if one song is playing, and I start a second one, both are playing at the same time. I've tried many ways to get this problem fixed, but none of them did work - how do I need to adapt it, so that the first one pauses or stops when a second one starts? Thanks!
You can do something like this. When a new audio is started, loop through the playlist array to find all the audio elements that are not the one that just got started and stop them.
Here is an example:
....
var method = pause ? 'pause' : 'play';
audio[method]();
if(!pause){
playlist.forEach(function(pl){
if(pl.audio !== audio){
pl.audio.pause();
pl.audio.currentTime = 0;
pl.control.innerHTML = 'Listen';
}
});
}
....

Pause is behaving incorrectly in the SoundCloud Javascript API

Note: Exactly this has been asked before here, and here, without satisfactory answers.
I'll try to explain the problem better here, and you can also read TLDR below. Here's a simple fiddle I created using Soundcloud JS sdk v3. I define global variables playerGlobal and playingId. During the first play, both of them are null, and clicking one of play or playNext buttons initializes those variables. Suppose, we click play first, the related track starts playing. When we click pause button soon after, pause() fires and uses the globalPlayer to pause the currently playing track. This works as expected.
When I click playNext, both global variables are updated as they should. The problem arises when I click play again. For some strange reasons, the track associated with play button plays from time starting at when I clicked pause above. When I click pauseagain, nothing happens.
TLDR: Click play -> pause -> playNext(works fine till here) -> play(strange remembering of last position happens) -> pause(nothing happens).
I'm including the snippet here as well.
SC.initialize({
client_id: 'MmZKx4l7fDwXdlL3KJZBiJZ8fLonINga'
});
//Get player
function getPlayer(url) {
return new Promise(function(resolve, reject) {
SC.stream(url)
.then(player => {
if (player.options.protocols[0] === 'rtmp')
player.options.protocols.splice(0, 1);
resolve(player);
});
});
}
//Global players and id to track current playing track
var playerGlobal = null;
var playingId = null;
function play() {
var id = '1';
//Resume playing if the same song is hit
if (playerGlobal !== null && id === playingId) {
console.log("-- using old player");
playerGlobal.play();
return;
}
//New player on a new song
var url = '/tracks/309689093';
getPlayer(url)
.then(player => {
if (playerGlobal) playerGlobal = null;
playerGlobal = player;
console.log("-- new player created");
playerGlobal.play();
playingId = id;
});
}
function pause() {
playerGlobal.pause();
}
function playNext() {
var id = '2';
//Resume playing if the same song is hit
if (playerGlobal !== null && id === playingId) {
console.log("-- using old player");
playerGlobal.play();
return;
}
//New player on a new song
var url = "/tracks/309689082";
getPlayer(url)
.then(player => {
if (playerGlobal) playerGlobal = null;
playerGlobal = player;
console.log("-- new player created");
playerGlobal.play();
playingId = '2';
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<button onClick="play()">play</button>
<button onClick="pause()">pause</button>
<button onClick="playNext()">playNext</button>
<script src="https://connect.soundcloud.com/sdk/sdk-3.1.2.js"></script>
</body>
</html>
I'm not sure why this wouldn't work. One of the ways I was able to make this work was by defining separate player, pause and id variables for both tracks. And then when a track is played after another, I set seek to position 0. Here's it. The problem of course is, this is just for reference, my use case involves many more tracks, and I didn't want to define separate variables for them all, until I'm sure why above code doesn't work and that it is the only way. Any hints are appreciated.

Categories

Resources