Async play sound in javascript? - javascript

how can I load asynchronously a sound in javascript ?
I want to play in loop severals sounds ans calculus in parallel.
This is the timeline :
10 seconds 7 seconds 10 seconds
|--------------------|------------------|--------------------|--- etc
| Sound1 playing |Sound2 playing | Sound1 playing |--- etc
| Do a calculus |Do a calculus | Do a calculus |--- etc
Sound1 and Sound2 last less than 5 seconds
Calculs last 1 second.
How can i do that in javascript ?
I must use Workers in HTML5 ?
Thanks.

Playing sounds asynchronously in JS is in fact pretty simple. You just have to create new Audios and play() them right away instead of play()ing global Audio objects. Here's an example:
function playSoundAsync(url){
new Audio(url).play();
}
So in your case, your code would look like this (with a hack involving setIntervals and setTimeouts for syncing up the sounds):
// For simplicity I'll use MP3 files readily available on the Internet
var sound1url = 'https://audio.code.org/win3.mp3';
var sound2url = 'https://audio.code.org/failure3.mp3';
var calculusUrl = 'https://audio.code.org/failure1.mp3';
setInterval(function() {
new Audio(sound1url).play();
new Audio(calculusUrl).play();
}, 17000);
setTimeout(function() {
setInterval(function() {
new Audio(sound2url).play();
new Audio(calculusUrl).play();
}, 17000);
}, 10000);
<button onclick="new Audio(sound1url).play();">Play sound1 individually</button>
<button onclick="new Audio(sound2url).play();">Play sound2 individually</button>
<button onclick="new Audio(calculusUrl).play();">Play calculus individually</button>
In addition, you might notice this when you click the Play ${sound} individually buttons rapidly: A new playback of ${sound} starts without waiting for or interrupting the current ${sound} playback! This allows you to create an acoustic mess like this:
var cacophony = setInterval(function(){new Audio('https://audio.code.org/win3.mp3').play();}, 25);
<button onclick="clearInterval(cacophony);">Click here to stop this cacophony</button>

You can easily make your audio play async (without using a timer) by using JS promises and async functions.
// makes playing audio return a promise
function playAudio(audio){
return new Promise(res=>{
audio.play()
audio.onended = res
})
}
// how to call
async function test(){
const audio = new Audio('<url>')
await playAudio(audio)
// code that will run after audio finishes...
}

I built on dorukayhan's answer, but my sounds were going to repeat thousands of times. I was afraid that creating multiple new Audio tags would eventually crash the system.
So I created a function that cycles through a bunch of existing audio tags.
The tags go somewhere in your HTML. I put mine before the end </body> tag. My sounds were short bleeps, so 10 tags were enough to play smooth, but you can experiment with the number of audio tags.
<audio id="audio0"></audio>
<audio id="audio1"></audio>
<audio id="audio2"></audio>
<audio id="audio3"></audio>
<audio id="audio4"></audio>
<audio id="audio5"></audio>
<audio id="audio6"></audio>
<audio id="audio7"></audio>
<audio id="audio8"></audio>
<audio id="audio9"></audio>
//declare a global counter in your script
<script>
var audiocount=0;
/*other code...*/
/*call to play sound*/
Playsound('mysound.wav')
/*other code*.../
/*add function to play sounds*/
function Playsound(MySoundPlay)
{
/*id() is a shortcut for getElementById*/
id('audio'+audiocount).src=MySoundPlay
id('audio'+audiocount).play()
audiocount++;
/*reset your counter according to no. of audio tags you created*/
if (audiocount>9){audiocount=0;}
}
/*shortcut function for getElementById*/
function id(myID){return document.getElementById(myID)}
</script>

Maybe you are searching for window.setInterval().

Related

Is it possible to check how long someone has played the mp3 sound?

I need to check how long someone played the mp3 file, I read about the .buffored function, which allows you to check how many times where or when it was stopped, but you have some option to count the listening time of mp3
const myAudio = document.getElementById('my-audio');
const bufferedTimeRanges = myAudio.buffered;
<audio id="my-audio" controls src="music.mp3">
</audio>
const audio = document.querySelector('audio');
audio.ontimeupdate = (event) => {
console.log('The currentTime attribute has been updated. Again.');
};
you can use the ontimeupdate event to receive currentTime information about the audio file. You can combine it with the onended event to count how many times the user ended the audio and you'll have the whole feature done.

Playing audio for a specific time in JavaScript

If you want to play some sounds and audio files (like for notification in button's click or for any other purpose) for a specific time ( like 2 seconds or 5 seconds regardless of how long or short that audio file is ) by your javascript code.
First, we create a DOM Audio object for our audio file by providing the path to that audio file.
then set loop to true, call play(), and at last call pause() after a specific time with the help of setTimeout().
funciton play( audio_path, time_in_milisec){
let beep = new Audio( audio_path);
beep.loop = true;
beep.play();
setTimeout(() => { beep.pause(); }, time_in_milisec);
}
play('beep.mp3', 2000);
Did you want to code this yourself, or are you looking for a library to make this easier? You might want to check out WadJS
let song = new Wad({source : 'https://www.myserver.com/audio/mySong.wav'})
song.play({
env: {
// The duration in seconds. It defaults to the length of the audio file, but you can manually set it lower to make the sound stop after a certain amount of time
hold: 10
}
})

How can I get a (very) short .mp3 audio file to play on every keystroke?

I am building a web-app which includes a form and (as a progressive enhancement / UX effect) I would like a keypress to fire a keystroke sound effect.
I have cut the .mp3 sound effect quite short (0.18s).
Nevertheless, there appears to be an audible delay when I test my setup on Firefox on my laptop (and not every keypress fires the sound effect).
On Firefox Mobile on my Android, very few keypresses fire the sound effect - maybe one keypress in every eight.
Am I trying to achieve something which can't be done using present web technology, or am I simply approaching this the wrong way?
Here is my setup:
var myInput = document.getElementsByTagName('input')[0];
var keypress = document.getElementsByClassName('keypress')[0];
function playSoundEffect() {
keypress.play();
}
myInput.addEventListener('keydown', playSoundEffect, false);
/* I've also tried...
myInput.addEventListener('keyup', playSoundEffect, false);
myInput.addEventListener('keypress', playSoundEffect, false);
*/
input {
width: 90vw;
}
<input type="text" placeholder="Type to hear the sound effect..." />
<audio class="keypress">
<source src="http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3" type="audio/mpeg">
</audio>
Second Attempt:
Thanks to the helpful comments below from #StackingForHeap and #zero298, I have refactored my setup:
var myInput = document.getElementsByTagName('input')[0];
function playSoundEffect() {
var jsKeypress = new Audio('http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3');
jsKeypress.play();
}
myInput.addEventListener('input', playSoundEffect, false);
input {
width: 90vw;
}
<input type="text" placeholder="Type to hear the sound effect..." />
This is definitely an improvement - creating a new Audio object each time allows for each one to start playing independently of any previously created Audio objects.
I have taken a look at your second attempt and it seems sound (excuse the pun). However, I just took a crack at it and I am able to get pretty good results with this:
This uses an audio file that I found on Freesound.org: UI Confirmation Alert, C4.wav.
This creates an AudioContext instead of just an Audio and uses the same, already loaded, buffer over and over again. I'm still trying to see if there is a performance gain there.
Looks like you have to make source nodes over and over again:
An AudioBufferSourceNode can only be played once; after each call to
start(), you have to create a new node if you want to play the same
sound again. Fortunately, these nodes are very inexpensive to create,
and the actual AudioBuffers can be reused for multiple plays of the
sound. Indeed, you can use these nodes in a "fire and forget" manner:
create the node, call start() to begin playing the sound, and don't
even bother to hold a reference to it. It will automatically be
garbage-collected at an appropriate time, which won't be until
sometime after the sound has finished playing.
/*jslint browser:true, esversion:6, devel:true*/
/*global AudioContext*/
(function() {
"use strict";
function main() {
let ctx = new AudioContext();
fetch("/res/audio/twinkle.wav")
.then(res => res.arrayBuffer())
.then((buffer) => {
return new Promise((resolve, reject) => {
ctx.decodeAudioData(buffer, (audioBuffer) => {
resolve(audioBuffer);
});
});
})
.then((audioBuffer) => {
document.addEventListener("keydown", (e) => {
console.log(e.keyCode);
if (e.keyCode === 37) { // The "left arrow" key
let source = ctx.createBufferSource();
source.buffer = audioBuffer;
source.connect(ctx.destination);
source.start(0);
}
});
});
}
document.addEventListener("DOMContentLoaded", main);
}());
I try doing the same process with the howler.js library, and it seems to work very well.
this is the example
var myInput = document.getElementsByTagName('input')[0];
var sound = new Howl({
src:['http://handsoffhri.org/.assets/theme/elements/mp3/keypress.mp3']
});
sound.play();
function playSoundEffect() {
sound.play();
}
myInput.addEventListener('keydown', playSoundEffect, false);

Playing Sound Instantly (without waiting to complete) on Javascript

I partially achieved playing sound with keys with html and .play. However it waits for the track to be completed and doesn't let me play instantly on every click. However I want to trigger the sound as soon as I press it each time.
<audio id=soundId1>
<source src=sound/Saaa.wav>
</audio>
<audio id=soundId2>
<source src=sound/Baaa.wav>
</audio>
$(document).ready(function() {
var sound = {
65 : 'soundId1',
23 : 'soundId2'
};
document.onkeydown = function(e) {
var soundId = sound[e.keyCode];
if (soundId) document.getElementById(soundId).play();
else console.log("key not mapped : code is", e.keyCode);
}
Is there a way of achieving it with the above approach only without using any library?
I also looked into howler.js (I don't know if it's proper use) but I couldn't really understand how I can add multiple tracks into the dictionary. I understand how I can put the files into sounds dictionary however how to call it and link it to keyCode system above? Or should I add a new Howl for each sound individually?
What is the proper way of achieving it?
This may work but I have not tried it:
function Sound(){
this.src = ""
}
function playSound(source){
var sound = new Sound();
sound.src = source;
sound.play()
}
A new sound instance is created instead of overriding the previous one.

HTML5 Audio Looping

I've been playing with HTML5 audio recently, and though I can get it to play the sound it only ever will play once. No matter what I try (setting the properties, event handlers, etc) I can't seem to get it to loop.
Here's the basic code I'm using:
//myAudio is declared at a global scope, so it doesn't get garbage collected.
myAudio = new Audio('someSound.ogg');
myAudio.loop = true;
myAudio.play();
I'm testing using Chrome (6.0.466.0 dev) and Firefox (4 beta 1), both of which seem happy to ignore my requests for looping. Any ideas?
UPDATE: The loop property is now supported in all major browsers.
While loop is specified, it is not implemented in any browser I am aware of Firefox [thanks Anurag for pointing this out]. Here is an alternate way of looping that should work in HTML5 capable browsers:
var myAudio = new Audio('someSound.ogg');
myAudio.addEventListener('ended', function() {
this.currentTime = 0;
this.play();
}, false);
myAudio.play();
To add some more advice combining the suggestions of #kingjeffrey and #CMS: You can use loop where it is available and fall back on kingjeffrey's event handler when it isn't. There's a good reason why you want to use loop and not write your own event handler: As discussed in the Mozilla bug report, while loop currently doesn't loop seamlessly (without a gap) in any browser I know of, it's certainly possible and likely to become standard in the future. Your own event handler will never be seamless in any browser (since it has to pump around through the JavaScript event loop). Therefore, it's best to use loop where possible instead of writing your own event. As CMS pointed out in a comment on Anurag's answer, you can detect support for loop by querying the loop variable -- if it is supported it will be a boolean (false), otherwise it will be undefined, as it currently is in Firefox.
Putting these together:
myAudio = new Audio('someSound.ogg');
if (typeof myAudio.loop == 'boolean')
{
myAudio.loop = true;
}
else
{
myAudio.addEventListener('ended', function() {
this.currentTime = 0;
this.play();
}, false);
}
myAudio.play();
Your code works for me on Chrome (5.0.375), and Safari (5.0). Doesn't loop on Firefox (3.6).
See example.
var song = new Audio("file");
song.loop = true;
document.body.appendChild(song);​
Simplest way is:
bgSound = new Audio("sounds/background.mp3");
bgSound.loop = true;
bgSound.play();
var audio = new Audio("http://rho.nu/pub/Game%20Of%20Thrones%20-%20Main%20Theme%20-%20Soundtrack.mp3");
audio.addEventListener('canplaythrough', function() {
this.currentTime = this.duration - 10;
this.loop = true;
this.play();
});
Just set loop = true in the canplaythrough eventlistener.
http://jsbin.com/ciforiwano/1/edit?html,css,js,output
Try using jQuery for the event listener, it will then work in Firefox.
myAudio = new Audio('someSound.ogg');
$(myAudio).bind('ended', function() {
myAudio.currentTime = 0;
myAudio.play();
});
myAudio.play();
Something like that.
I did it this way,
<audio controls="controls" loop="loop">
<source src="someSound.ogg" type="audio/ogg" />
</audio>
and it looks like this
This works and it is a lot easier to toggle that the methods above:
use inline: onended="if($(this).attr('data-loop')){ this.currentTime = 0; this.play(); }"
Turn the looping on by $(audio_element).attr('data-loop','1');
Turn the looping off by $(audio_element).removeAttr('data-loop');
You could try a setInterval, if you know the exact length of the sound. You could have the setInterval play the sound every x seconds. X would be the length of your sound.
Everyone knows now if you type:
<audio controls="controls" loop="loop">
<source src="someSound.ogg" type="audio/ogg" />
</audio>
It will play the song and it will be looping
But there is a shorter way to play the song continuously:
<audio controls loop src="someSound.ogg"></audio>

Categories