Handling 'onkeydown' event - javascript

I've added onkeydown event listener to document, and when the event fires, I play an audio file. This audio file is a gun sound. If you tap the key once, it plays fine, but if you press and hold, the audio repeats incredibly fast. I've solved that with a simple playing condition check, but I have another problem. If I press and hold, only after the first shot, it sounds like the gun is firing repeatedly. For example, it goes like ta-tatatatata.
How can I fix my machine gun? Make it fire like tatatata.
Demo
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function playAudio(file) {
if (!playing) {
var audio = new Audio(file);
audio.play();
playing = true;
setTimeout(function() {
playing = false;
}, 100);
}
}
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
playAudio(weapons.aug.audio);
break;
}
}

Hey let me know if it works I hope it helps !
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function loadAudio(file) {
var audio = new Audio(file);
return audio;
}
function playAudio(file) {
audio.play();
}
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
var gunsound = loadAudio(weapons.aug.audio);
playAudio(gunsound);
setTimeout(function() {},200);
break;
}
}

There should probably be a better way with a gameLoop and less globals, but wait for the ended event of the audio object to tag playing at false and be sure not to start playing your sound unless it's finished.
var playing = false;//tag playing as false by default
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function playAudio(file) {
if (!playing) {
audio = new Audio(file);
playing = true;
setTimeout(function(){
playing = false;
},400);
audio.play();
}
}
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
playAudio(weapons.aug.audio);
break;
}
}

akinuri, this was my first answer, but I deleted it (I thought you wanted to play the whole sound), so I undeleted it, it could help you (it will fix the delay bug of keydown).
You should use a gameLoop (common pattern in game development), with requestAnimationFrame.
Don't trigger events directly from player's input, set states that will be processed inside the gameLoop (which should be in charge of updating positions, rendering ...)
var playing = false;
var keyStatePressed = false;//set state at false at init
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A"
}
};
function playAudio(file) {
if (!playing) {
var audio = new Audio(file);
audio.play();
playing = true;
setTimeout(function() {
playing = false;
}, 100);
}
}
//flag at true on keydown
document.onkeydown = function(e) {
switch(e.keyCode) {
case 65:
keyStatePressed = true;
break;
}
}
//run a game loop
function gameLoop(){
if(keyStatePressed === true){
playAudio(weapons.aug.audio);
keyStatePressed = false;//reset state
}
//run this in 16ms (see more on game loops and requestAnimationFrame)
requestAnimationFrame(gameLoop);
}
//launch the gameLoop
gameLoop();

I started to code this before thinking about it, and that was my first mistake. I've solved the problem now. All I had to do was simply mimic the FPS games' fire command. Start an interval with the mousedown and clear the interval with mouseup.
Instead of using mouse click, I had to use key press. So when I press a key (and hold), an interval starts and plays the audio repeatedly. When I release the key, interval is cleared, audio stops.
Demo
var weapons = {
aug : {
audio : "weapons/aug-1.wav",
icon : "e",
key : "A",
interval : null,
firstShot : false,
}
};
function playAudio(file, weapon) {
if (!weapon.interval) {
if (!weapon.firstShot) {
var audio = new Audio(file);
audio.play();
weapons.aug.firstShot = true;
}
weapon.interval = setInterval(function() {
var audio = new Audio(file);
audio.play();
}, 150);
}
}
document.onkeydown = function(e) {
switch (e.keyCode) {
case 65:
playAudio(weapons.aug.audio, weapons.aug);
break;
}
}
document.onkeyup = function(e) {
switch (e.keyCode) {
case 65:
clearInterval(weapons.aug.interval);
weapons.aug.interval = null;
weapons.aug.firstShot = false;
break;
}
}

Related

Tone.js app - audio won't start until second click

I'm aware that I need a user action to start Web Audio on mobile. However I have followed the recommendation to resume (in the case of Tone.js call Tone.start()) on user action but it still don't work.
Here are the relevant parts of my code:
keyElement.addEventListener("touchstart", notePressed, false);
keyElement.addEventListener("touchend", noteReleased, false);
function notePressed(event) {
let dataset = event.target.dataset;
if (!dataset["pressed"]) {
let octave = +dataset["octave"];
oscList[octave][dataset["note"]] = playTone(dataset["frequency"]);
dataset["pressed"] = "yes";
}
event.preventDefault(); event.stopPropagation();
}
function noteReleased(event) {
let dataset = event.target.dataset;
if (dataset && dataset["pressed"]) {
let octave = +dataset["octave"];
oscList[octave][dataset["note"]].triggerRelease();
delete oscList[octave][dataset["note"]];
delete dataset["pressed"];
}
}
function playTone(freq) {
let synth = new Tone.Synth().toDestination();
let now = Tone.now();
Tone.start();
synth.triggerAttack(freq, now);
return synth;
}
It works fine on the second touch, but after the first touch I get a warning about the audio context being suspended, even though the standard is that it is resumed when start() is called by a user action. I'm stumped here.
That Tone code looks correct. I simplified it in a CodePen and it worked on mobile on first click (iOS).
var keyElement = document.getElementById("play");
keyElement.addEventListener("touchstart", notePressed, false);
keyElement.addEventListener("touchend", noteReleased, false);
// Added mouse ELs to test on non-mobile.
keyElement.addEventListener("mousedown", notePressed, false);
keyElement.addEventListener("mouseup", noteReleased, false);
var pressedTone;
function notePressed(event) {
console.log("notePressed");
pressedTone = playTone(440);
event.preventDefault();
event.stopPropagation();
}
function noteReleased(event) {
pressedTone.triggerRelease();
}
function playTone(freq) {
console.log("playTone", freq);
let synth = new Tone.Synth().toDestination();
let now = Tone.now();
Tone.start();
synth.triggerAttack(freq, now);
return synth;
}
Here's the link to the CodePen: https://codepen.io/joeweiss/pen/gOmeQVW

Need simplified function for onclick button sounds

This is my first question here sorry if I make any mistake.
I want to know how to simplify an action (function), I have a drumkit with different buttons and sounds, I'm using this code: FIDDLE
JS
function play() {
var audio = document.getElementById('audio1');
if (audio.paused) {
audio.play();
} else {
audio.currentTime = 0
}
}
How can I write just one function and play different sounds for every button?
You could try firing multiple functions from one function. Something like:
<button onClick="play()">song1</button>
<button onClick="play()">song2</button>
<button onClick="play()">song3</button>
function someFunc() {
function audioOne() {
var audio = document.getElementById('audio1');
if (audio.paused) {
audio.play();
}
else{
audio.currentTime = 0
};
function audioTwo() {
var audio = document.getElementById('audio2');
if (audio.paused) {
audio.play();
}
else{
audio.currentTime = 0
};
function audioThree() {
var audio = document.getElementById('audio3');
if (audio.paused) {
audio.play();
}
else{
audio.currentTime = 0
};
}
Alternatively you could also try something like:
var audio = document.getElementById("id");
audio.addEventListener("click", function(){alert("audio1 triggered")}, false);
audio.addEventListener("click", function(){alert("audio2 triggered")}, false);
function play(id) {
var audio = document.getElementById(id);
if (audio.paused) {
audio.play();
}else{
audio.currentTime = 0
}
call it like this:
play('audio1');
play('audio2');
If i understand your question correctly this should work for your use-case. You basically just pass the elements id as parameter. Everytime you call the function the logic will apply to the element you specified as parameter in your function call.

Playing array of audio in sequence not working even if I have ended function

I'm trying to play all my audio files in sequence with the duration between each audio. Right now, I can play the first audio, but I have to click the play button again in order to play the second audio. ended function should handle this, shouldn't it? Appreciate your kindness and help.
var playList = [{
"duration": 1000,
"value": "0_silence"
}, {
"duration": 4000,
"value": "1_hello"
}, {
"duration": 6000,
"value": "2_how_old"
}];
$(document).ready(function() {
var audioElement = document.createElement('audio');
var i = 0;
$("#play").on("click", function(){
audioElement.addEventListener("ended", playAudio(playList));
function playAudio() {
if(i < playList.length) {
audioElement.setAttribute('src', 'sound/' + playList[i].value + '.wav');
setTimeout(function (){audioElement.play(); playAudio, playList[i].duration;});
i++;
} else {
i = 0;
}
}
});
// playAudio(playList);
// });
//
// function playAudio() {
// var audioElement = document.createElement('audio');
// audioElement.addEventListener("ended", playAudio(playList));
// if (i < playList.length) {
//
// var audioElement = document.createElement('audio');
// audioElement.addEventListener("ended", playAudio(playList));
// audioElement.setAttribute('src', 'sound/' + playList[i].value + '.wav');
// audioElement.play();
// setTimeout(playAudio, playList[i].duration);
// i++;
// } else {
// i = 0; // reset i to zero to start over if button clicked again
// }
// }
}
The first error is in this line:
audioElement.addEventListener("ended", playAudio(playList));
Here the function playAudio() is called and the result from it (undefined in this case) is handed to the addEventListener() as argument, which of course won't work. If the playList is global simply change it to:
audioElement.addEventListener("ended", playAudio); // function is now referenced
In addition, the handler is called every time the play button is clicked. Only add it once when the element is created.
There is also an error in the setTimeout() call, as shown in the post, as it misses a time argument and contains invalid statements inside the function block.
However, it shouldn't be necessary to use this call as calling play() on the element will, in newer browsers, cue up the call. For older browsers you may need to use the canplay event. setTimeout() is not recommended here in either case.
You should also call load() before play() to reset and cleanup the element, and to start loading the new source.
And finally, if the intention is to play the list in a loop the list will fail when last track has been played as i is reset in a separate else-block which does not invoke play.
You might want to consider how the play button behaves in order to prevent it from loading samples while playing (ie. toggle pause/play). I wrapped up the fixes above except from the play-button (I rewired it to share the playAudio() function) here using random short samples:
var playList = [{
"duration": 1000,
"value": "http://sampleswap.org/samples-ghost/SFX%20and%20UNUSUAL%20SOUNDS/ELECTRO%20and%20SYNTHETIC/257[kb]50s_space_telemetry.aif.mp3"
}, {
"duration": 4000,
"value": "http://sampleswap.org/samples-ghost/SFX%20and%20UNUSUAL%20SOUNDS/ELECTRO%20and%20SYNTHETIC/85[kb]analog_alarm_up.aif.mp3"
}, {
"duration": 6000,
"value": "http://sampleswap.org/samples-ghost/SFX%20and%20UNUSUAL%20SOUNDS/ELECTRO%20and%20SYNTHETIC/42[kb]alien_alarm.aif.mp3"
}];
$(document).ready(function() {
var audioElement = document.createElement('audio');
// reference function instead of calling it. Also, only set it once.
audioElement.addEventListener("ended", playAudio);
var i = 0;
$("#play").on("click", playAudio); // rewired to share playAudio()
function playAudio() {
var entry = playList[i++]; // get current entry, increment i
if (i >= playList.length) i = 0; // if i=> length, reset
audioElement.src = entry.value; // <- for demo samples only. 'sound/' + entry.value + '.wav';
audioElement.load(); // cleanup old fun, invoke loading of new
audioElement.play(); // cue up play
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id=play>Play</button>

Controlling when Vimeo thumbnail turns off and video begins

I'm looking into the vimeo embed api. I want the video to autoplay on load which I have working but currently what happens is like this:
player loads then autoplays [event "ready"]
thumbnail removed and shows black for about a second
video begins playing [event "playProgess"]
The problem is the second step. I'm trying to eliminate that black screen between when the thumbnail hides (when play is initiated) to when the video actually appears and starts playing.
The way I figure it can be solved is to keep the thumbnail around and trigger the thumbnail hide on the first "playProgress" but I can't seem to find anyway to control when the thumbnail turns on or off.
Is this possible to control? I'm aware that I can pull the thumbnail and overlay it over the iframe but I'm hoping for a cleaner fix (keep it all contained to the iframe).
Here's a pen with api running:
http://codepen.io/mattcoady/pen/KMzZMZ
$(function() {
var player = $('iframe');
var playerOrigin = '*';
var status = $('.status');
// Listen for messages from the player
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
}
else {
window.attachEvent('onmessage', onMessageReceived, false);
}
// Handle messages received from the player
function onMessageReceived(event) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {
return false;
}
if (playerOrigin === '*') {
playerOrigin = event.origin;
}
var data = JSON.parse(event.data);
console.log(data.event);
switch (data.event) {
case 'ready':
onReady();
break;
case 'playProgress':
onPlayProgress(data.data);
break;
case 'pause':
onPause();
break;
case 'finish':
onFinish();
break;
case 'play':
onPlay();
break;
}
}
// Call the API when a button is pressed
$('button').on('click', function() {
post($(this).text().toLowerCase());
});
// Helper function for sending a message to the player
function post(action, value) {
var data = {
method: action
};
if (value) {
data.value = value;
}
var message = JSON.stringify(data);
player[0].contentWindow.postMessage(message, playerOrigin);
}
function onReady() {
status.text('ready');
post('play');
post('addEventListener', 'pause');
post('addEventListener', 'finish');
post('addEventListener', 'playProgress');
}
function onPause() {
status.text('paused');
}
function onFinish() {
status.text('finished');
}
function onPlay(){
alert('play')
}
function onPlayProgress(data) {
status.text(data.seconds + 's played');
}
});
What I ended up going with my hacky fix. It's pulls the thumbnail and lays it over the video. When my script detects the 'playProgress' event that means the video is actually playing. I use jQuery to fade away the thumbnail cover.
http://codepen.io/mattcoady/pen/YWqaWJ
$(function() {
var player = $('iframe');
var playerOrigin = '*';
var videoId = 76979871;
player.attr('src', 'https://player.vimeo.com/video/' + videoId + '?api=1&player_id=player1&background=1&autoplay=1&loop=1');
// Listen for messages from the player
if (window.addEventListener) {
window.addEventListener('message', onMessageReceived, false);
} else {
window.attachEvent('onmessage', onMessageReceived, false);
}
$.getJSON('http://vimeo.com/api/v2/video/' + videoId + '.json', {jsonp: 'callback',dataType: 'jsonp'}, function(data) {
var thumbnail = document.createElement('img');
thumbnail.src = data[0].thumbnail_large;
thumbnail.style.width = document.querySelector('#player1').offsetWidth + 'px';
thumbnail.style.height = document.querySelector('#player1').offsetHeight + 'px';
document.querySelector('#vimeo-thumb-container').appendChild(thumbnail);
})
// Handle messages received from the player
function onMessageReceived(event) {
// Handle messages from the vimeo player only
if (!(/^https?:\/\/player.vimeo.com/).test(event.origin)) {return false;}
if (playerOrigin === '*') { playerOrigin = event.origin; }
var data = JSON.parse(event.data);
switch (data.event) {
case 'ready':
onReady();
break;
case 'playProgress':
onPlayProgress(data.data);
break;
}
}
// Helper function for sending a message to the player
function post(action, value) {
var data = { method: action };
if (value) {data.value = value;}
var message = JSON.stringify(data);
player[0].contentWindow.postMessage(message, playerOrigin);
}
function onReady() {
post('play');
post('addEventListener', 'playProgress');
}
function onPlayProgress(data) {
$('#vimeo-thumb-container').fadeOut(250);
}
});

How to play an audio file in a infinite loop using jQuery

Right now I have a piece of code (below) which allows me to play audio files on button click. However, I would like to have the file playing in a infinite loop until I click click the button "Stop" button.
How can I achieve that?
var audiotypes={
"mp3": "audio/mpeg",
"mp4": "audio/mp4",
"ogg": "audio/ogg",
"wav": "audio/wav"
}
function ss_soundbits(sound){
var audio_element = document.createElement('audio')
if (audio_element.canPlayType){
for (var i=0; i<arguments.length; i++){
var source_element = document.createElement('source')
source_element.setAttribute('src', arguments[i])
if (arguments[i].match(/\.(\w+)$/i))
source_element.setAttribute('type', audiotypes[RegExp.$1])
audio_element.appendChild(source_element)
}
audio_element.load()
audio_element.playclip=function(){
audio_element.pause()
audio_element.currentTime=0
audio_element.play()
}
return audio_element
}
}
var audio = ss_soundbits('audio/file.mp3');
var start_audio= function() {
audio.playclip();
};
Capture the ended event on the audio element, and then make it play again.
audio_element.addEventListener('ended', function() {
this.currentTime = 0;
this.play();
}, false);
This should be sufficient for you.
edit
As pointed out by #A. Wolff, I made an oversight wherein clicking stop would fire the ended event. Setting a global boolean flag will resolve this.
var stopped = false;
audio_element.addEventListener('ended', function() {
if(!stopped){
this.currentTime = 0;
this.play();
}
}, false);
var stop_button = document.getElementByID('stop');
stop.addEventListener('click', function(){
stopped = true
audio_element.stop();
});
Sorry for my comment - only addition: audio_element.play() may not work on mobile browsers without manual click

Categories

Resources