Web Audio API Gain node envelope generators - javascript

Im trying to make an envelope generator for my oscillator using automation on the gainNode and I want to be able to trigger the envelope whenever an event happens ( in this case a button click ).
But it seems like its working only the first time I click.
http://jsfiddle.net/ehsanziya/T9mV2/
var context = new webkitAudioContext();
var osc = context.createOscillator();
var gain = context.createGainNode();
var now = context.currentTime;
osc.frequency.value = 100;
osc.type = "sine";
osc.connect(gain);
osc.noteOn(0);
gain.connect(context.destination);
gain.gain.value = 0;
var trigger = document.getElementById('play');
trigger.addEventListener('click', function(){
gain.gain.setValueAtTime(gain.gain.value, now);
gain.gain.linearRampToValueAtTime( 1.0, now + 2.0 );
gain.gain.linearRampToValueAtTime ( 0.0, now + 4.0 );
});
Why is it like that? And what is the best way to create envelope generators that can be triggered by events?

Just move the line retrieving context.currentTime inside the click event listener.
trigger.addEventListener('click', function(){
var now = context.currentTime;
...
The currentTime property exposes audio clock and so is updated in real time.

Found the solution.
But given that the oscillator is always on, there should be a way to reset the phase of the oscillator every time its being triggered ( Phase Bashing ).
$(document).ready(function(){
var context = new webkitAudioContext();
var osc = context.createOscillator();
var gain = context.createGain();
osc.connect(gain);
gain.connect(context.destination);
osc.start(0);
gain.gain.value = 0;
var oscStart = function(){
var now = context.currentTime;
gain.gain.cancelScheduledValues( now );
gain.gain.setValueAtTime(gain.gain.value, now);
gain.gain.linearRampToValueAtTime(1 , now + 0.2);
};
var oscOff = function(){
var now = context.currentTime;
gain.gain.cancelScheduledValues( now );
gain.gain.setValueAtTime(gain.gain.value, now);
gain.gain.linearRampToValueAtTime(0 , now + 0.2);
};
$('#button').hover(oscStart , oscOff);
});
http://jsfiddle.net/ehsanziya/JJqNU/

Related

Ring modulation using web audio api - javascript

I would like to ask if someone could help me with ring modulation using javascript
Here is my code. I am not sure if I am doing this right. On start button it only plays a oscillator with gain. No mix with audio file.
I tried do something like this GitHub source
Thanks
function audioFileLoader(fileDirectory, impulseFileDirectory) {
var audioContext = new AudioContext();
var soundObj = [];
soundObj.fileDirectory = fileDirectory;
soundObj.impulseFileDirectory = impulseFileDirectory;
// buffer loader code
var getSound = new XMLHttpRequest();
getSound.open("GET", soundObj.fileDirectory, true);
getSound.responseType = "arraybuffer";
getSound.onload = function() {
audioContext.decodeAudioData(getSound.response, function(buffer) {
soundObj.soundToPlay = buffer;
});
}
getSound.send();
soundObj.play = function() {
var source = audioContext.createBufferSource();
source.buffer = soundObj.soundToPlay;
var oscillator = audioContext.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.value = 500;
var gainNode = audioContext.createGain();
gainNode.gain.value = 0.5;
oscillator.connect(gainNode);
source.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.start(audioContext.currentTime);
};
return soundObj;
};
var example = audioFileLoader("audio/AcGtr.wav");
document.getElementById('ringmodulation').addEventListener("click", example.play,
false);
Your code never plays the buffer source, it's missing source.start(). Also var soundObj = [] should be var soundObj = {}.

Removing console.log Breaks Code with Web Audio API

I'm playing with the Web Audio API and using this code:
function playTones(startFreq, numNotes) {
if (numNotes === 0) return;
var osc = AC.createOscillator();
osc.frequency.value = startFreq;
osc.onended = function() {
playTones(startFreq + 20, numNotes-1);
};
console.log(osc.onended);
osc.connect(AC.destination);
osc.start();
osc.stop(AC.currentTime + 1);
}
It's just supposed to play a series of notes, each one 20Hz higher than the previous. However, when I remove the console.log it doesn't work; it only plays the first note and then stops with the onended callback never being called. Why would this happen? Thanks for any and all help.
Hey you could try this:
var AC = new AudioContext();
function playTones(startFreq, numNotes) {
if (numNotes === 0) return;
var osc = AC.createOscillator();
osc.frequency.value = startFreq;
// console.log(osc.onended);
osc.connect(AC.destination);
osc.start();
osc.stop(AC.currentTime + 1);
setTimeout(function(){playTones(startFreq+20,10)},1000);
}
playTones(200, 10);

How can I get an AudioBufferSourceNode's current time?

When working with audio elements (<audio>) or contexts(AudioContext), you can check their currentTime property to know exactly the play time of your buffer.
All of this is fine an dandy until I created multiple sources (or AudioBufferSourceNode) in a single AudioContext.
The sources could be played at different times, therefore I would need to know their corresponding currentTime's, to illustrate:
Some base code for you to work off:
buffer1 = [0,1,0]; //not real buffers
buffer2 = [1,0,1];
ctx = new AudioContext();
source1 = ctx.createBufferSourceNode();
source1.buffer = buffer1;
source1.connect(ctx.destination);
source1.start(0);
source2 = ctx.createBufferSourceNode();
source2.buffer = buffer2;
source2.connect(ctx.destination);
setTimeout(1000/*some time later*/){
source2.start(0);
}
setTimeout(1500/*some more time later*/){
getCurrentTime();
}
function getCurrentTime(){
/* magic */
/* more magic */
console.log("the sources currentTime values are obviously 1500 (source1) and 500 (source2).");
}
What I usually do is create a wrapper for the audio source node that keeps track of the playback state. I've tried to minimise the code below to show the basics.
The core idea is to keep track of the time the sound is started and the time the sound is 'paused' and use those values to get the current time and to resume playback from the paused position.
I put a working example on codepen
function createSound(buffer, context) {
var sourceNode = null,
startedAt = 0,
pausedAt = 0,
playing = false;
var play = function() {
var offset = pausedAt;
sourceNode = context.createBufferSource();
sourceNode.connect(context.destination);
sourceNode.buffer = buffer;
sourceNode.start(0, offset);
startedAt = context.currentTime - offset;
pausedAt = 0;
playing = true;
};
var pause = function() {
var elapsed = context.currentTime - startedAt;
stop();
pausedAt = elapsed;
};
var stop = function() {
if (sourceNode) {
sourceNode.disconnect();
sourceNode.stop(0);
sourceNode = null;
}
pausedAt = 0;
startedAt = 0;
playing = false;
};
var getPlaying = function() {
return playing;
};
var getCurrentTime = function() {
if(pausedAt) {
return pausedAt;
}
if(startedAt) {
return context.currentTime - startedAt;
}
return 0;
};
var getDuration = function() {
return buffer.duration;
};
return {
getCurrentTime: getCurrentTime,
getDuration: getDuration,
getPlaying: getPlaying,
play: play,
pause: pause,
stop: stop
};
}
Old thread (and very useful! thanks!) , but maybe worth mentioning that in the example above instead of
function update() {
window.requestAnimationFrame(update);
info.innerHTML = sound.getCurrentTime().toFixed(1) + '/' + sound.getDuration().toFixed(1);
}
update();
it may be more precise and less resource intensive to use a createScriptProcessor
like explained in this post
const audioBuffer = await audioContext.decodeAudioData(response.data);
const chan = audioBuffer.numberOfChannels;
const scriptNode = audioContext.createScriptProcessor(4096, chan, chan);
scriptNode.connect(audioContext.destination);
scriptNode.onaudioprocess = (e) => {
// ---> audio loop <----
};
[Update]
Note: As of the August 29 2014 Web Audio API spec publication, this feature has been marked as deprecated, and was replaced by AudioWorklet (see AudioWorkletNode). https://developers.google.com/web/updates/2017/12/audio-worklet

Using Keyboard input for 2 objects

edit: SOLVED
I'm a highschool student in Japan trying to learn how to program.
I recently viewed https://vimeo.com/105955605 this video, and decided I could use the beginning section to start building pong in javascript.
I'm pretty much a complete novice with programming and/or javascript and I still have a long way to go.
I got Player1 (left paddle) to work on its own, so I figured I could just copy paste, mess with a couple things, and make Player2. However, now Player2 moves when I press w/s, but Player1 no longer moves.
I've tried creating 2 separate keyboarder() functions, using this.keyboarder from Player1 in player2 (Player2.keyboarder = Player1.keyboarder() ), and declaring/calling keyboarder() before doing anything else.
HTML:
<html>
<head>
<meta charset="UTF-8">
<title>Pong</title>
<link type="text/css" rel="stylesheet" href="css/stylesheet.css">
</head>
<body>
<canvas id="screen" width="310" height="210"></canvas>
<script src="js/pong.js"></script>
</body>
</html>
JavaScript:
;(function(){
//Main game function
//tells objects in bodies array to update.
//stores gameSize pulled from canvasId
var Game = function(canvasId){
var canvas = document.getElementById(canvasId);
var screen = canvas.getContext('2d');
var gameSize = {x: canvas.width, y: canvas.height};
var self = this;
//bodies array
this.bodies = [new Player1(this, gameSize), new Player2(this, gameSize)];
//update function
var tick = function(){
self.update();
self.draw(screen,gameSize);
requestAnimationFrame(tick);
};
tick();
};
//constructer for game() function. tells bodies to update, and draw
Game.prototype = {
update: function(){
for(var i =0 ; i < this.bodies.length; i++){
this.bodies[i].update();
}
},
draw:function(screen,gameSize){
screen.clearRect(0,0,gameSize.x,gameSize.y);
for(var i =0 ; i < this.bodies.length; i++){
drawRect(screen, this.bodies[i]);
}
}
};
//P1 object, declares size and start position of P1
var Player1= function(game, gameSize){
this.size = {x:30,y:gameSize.y / 3};
this.game = game;
this.gameSize = gameSize;
this.center = {x: 0, y:gameSize.y/2};
this.keyboarder = new Keyboarder();
requestAnimationFrame(this.update);
};
//constructor for P1, updates position based on keyboard input
Player1.prototype = {
update:function(){
if (this.keyboarder.isDown(this.keyboarder.KEYS.DOWN) && this.center.y < (5*this.gameSize.y / 6)){
this.center.y += 4;
}else if(this.keyboarder.isDown(this.keyboarder.KEYS.UP) && this.center.y > this.size.y /2 ){
this.center.y -= 4;
}
}
};
//P2, same as P1 aside from position
var Player2= function(game, gameSize){
this.size = {x:30,y:gameSize.y / 3};
this.game = game;
this.gameSize = gameSize;
this.center = {x: gameSize.x, y:gameSize.y/2};
this.keyboarder = new Keyboarder();
requestAnimationFrame(this.update);
};
//constructor for P2, same as P1
Player2.prototype = {
update:function(){
if (this.keyboarder.isDown(this.keyboarder.KEYS.S) && this.center.y < (5*this.gameSize.y / 6)){
this.center.y += 4;
}else if(this.keyboarder.isDown(this.keyboarder.KEYS.W) && this.center.y > this.size.y /2 ){
this.center.y -= 4;
}
}
};
//Draw function, draws object
var drawRect = function(screen, body){
screen.fillRect(body.center.x - body.size.x /2,
body.center.y - body.size.y /2, body.size.x,body.size.y);
};
//Keyboard input function
//reads if keys are being pressed and takes the event code
//isDown() returns boolean of key down = true, key up = false
var Keyboarder = function(
){
var keyState = {};
window.onkeydown = function(e){
keyState[e.keyCode] = true;
};
window.onkeyup = function(e){
keyState[e.keyCode] = false;
};
this.KEYS = {DOWN: 37, UP:39,W:87 , S: 83};
this.isDown = function(keyCode){
return keyState[keyCode] === true;
};
};
//calls game() function when the page has loaded.
window.onload = function(){
new Game("screen")
};
})();
Sorry if this is bad protocol for using stackoverflow, I'm also new to asking questions here.
The problem is you have two Keyboarder instances, and they're both binding to the key events by assigning a handler directly to them - this will overwrite any other handlers. There's two ways to fix it:
1: Don't assign directly, instead use addEventListener, eg:
window.addEventListener('keydown', function(e){
keyState[e.keyCode] = true;
});
2: Use the same keyboarder instance for both players:
var kb = new Keyboarder();
this.bodies = [new Player1(this, gameSize, kb), new Player2(this, gameSize, kb)];
and in your player object assign it to this 3rd parameter. Even if you go this route, I would still advise on using addEventListener as well simply to ensure that the events can be bound to in multiple places if needed.
On another point, you should also be able to refactor your players into a single function and create two instances of it with different parameters, but that sort of thing is better dealt with over on Code Review.
When you create two Keyboarder instances, one collides with the other, because of this:
window.onkeydown = function(e){
keyState[e.keyCode] = true;
};
window.onkeyup = function(e){
keyState[e.keyCode] = false;
};
When you create one Keyboarder, the event listeners the previous one attached are overriden. Try something like this:
window.addEventListener('keydown', function(e){
keyState[e.keyCode] = true;
});
window.addEventListener('keyup', function(e){
keyState[e.keyCode] = false;
});
This ensures there are listeners for every instance of the Keyboarder.

Access event target in EaselJS

I'm an EaselJS newbie. I'm running version 0.7.1. I have a simple game that generates tiles. I would like to remove those tiles whenever they are clicked on. I know my click handler is called for each click event, but my tiles aren't being removed. What am I doing wrong?
c99.Game = (function() {
function Count99Game(){
this.canvas = document.getElementById('game-canvas');
this.stage = new createjs.Stage(this.canvas);
var totalTiles = 10;
for(var i = totalTiles; i>0; i--){
var tile = new c99.Tile(i);
this.stage.addChild(tile);
tile.x = Math.random()*(this.canvas.width - tile.width);
tile.y = Math.random()*(this.canvas.height - tile.height);
tile.addEventListener("click", function(event){
alert("Clicked");
this.stage.removeChild(event.target);
this.stage.update();
}.bind(this));
}
this.stage.update();
}
return Count99Game;
})();

Categories

Resources