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.
Related
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>
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 />
I built a simple matching game and added audio files for several cards like this:
cardList.map((cardItem) => {
var card = document.createElement("li");
card.classList.add("card");
var img = document.createElement("img");
img.src = [cardItem.img]
card.appendChild(img);
//audio starting here
audio = new Audio(cardItem.sound);
card.appendChild(audio);
audio.id += ' ' + cardItem.card;
deck.append(card);
card.className += ' ' + cardItem.name;
});
}
And in my startGame() function I added an eventListener to add audio.play(); on click.
So now, when I open dev tools, I can see audio elements for my cards but when I try and click on them, I'm only ever getting one audio file for all cards and not the audios I actually added to that specific element.
That's my GitHub file: https://github.com/cMikolai/guitar-colour-system-game/blob/master/js/app.js
Could anybody try and help me figure out how I could play the sound I added to the card I am clicking on?
I downloaded your code and played around. The code for playing audio is in the function startGame() in your app.js file.
app.js:
audio = []; // local variable declaration
...
...
// some code
function startGame() {
// some code
for (var i = 0; i < clickedCard.length; i++) {
clickedCard[i].addEventListener("click", function( event ) {
this.classList.add("open", "show");
openCards.push(this.getAttribute("class"));
audio.load();
audio.play(); // this is the line playing the audio
matchCards();
starRating();
}, false);
}
}
As you can see, you are not playing audio from the selected card but from the local variable audio.
So, you have to identify the selected card and play its respective audio instead of playing audio from the local variable.
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';
}
});
}
....
I'm using the HTML5 voice API for a project. I'm having trouble trying to set the result array: event.results.
I'm listening for '[A-H][0-9] "INTO" [A-H][0-9] "GO"'. When I hear "GO", I want to reset the events.result array. I have to listen for the same input again, but the first input cannot be removed from the event.results array.
I've tried:
event.results = [];
event.results.length = 0;
None of these work. I've also looked up the API and I can't find the solution to what I'm trying to do.
A similar example to what I want to do, would be if it was listening for any sequence of words. But when it heard the word "Cancel", it would forget everything it had just heard.
I hope this makes sense and any help is greatly appreciated.
It seems that the results are held in a SpeechRecognitionList, but I couldn't find any api for manipulating the list itself.
What does work is stopping and restarting the recognition, but that's problematic if you're not using a https-site (on a plain http site the browser always asks for permission when restarting recognition).
Here's an example:
<html>
<head>
<title>voice api test</title>
</head>
<body>
<h1>voice api test</h1>
<main></main>
<script>
(function startRecognizing() {
var main = document.querySelector('main');
var recognition = new webkitSpeechRecognition();
recognition.continuous = true;
recognition.addEventListener('result', function (event) {
var lastResult = event.results[event.results.length - 1];
if (lastResult[0].transcript.indexOf('cancel') > -1) {
recognition.stop();
while (main.children.length) main.removeChild(main.children[0]);
startRecognizing();
} else {
var p = document.createElement('p');
p.appendChild(document.createTextNode(lastResult[0].transcript));
main.appendChild(p);
}
});
recognition.start();
}());
</script>
</body>
</html>