I'm making a simple game in Javascript, in which when an object collides with a wall, it plays a "thud" sound. That sound's loudness depends on the object's velocity (higher velocity => louder sound).
The play function:
playSound = function(id, vol) //ID of the sound in the sounds array, volume/loudness of the sound
{
if (vol) //sometimes, I just want to play the sound without worrying about volume
sounds[id].volume = vol;
else
sounds[id].volume = 1;
sounds[id].play();
}
How I call it:
playSound(2, Math.sqrt(p.vx*p.vx + p.vy*p.vy)/self.TV); //self.TV stands for terminal velocity. This calculates the actual speed using the basic Pythagora's theorem and then divides it by self.TV, which results in a number from 0 to self.TV. 2 is the id of the sound I want to play.
In Chrome, things work quite well. In Firefox, though, each time a collision with a wall happens (=> playSound gets called), there's a pause lasting almost half a second! At first, I thought that the issues were at Math.sqrt, but I was wrong. This is how I tested it:
//playSound(2, 1); //2 Is the id of the sound I want to play, and 1 is max loudness
Math.sqrt(p.vx*p.vx + p.vy*p.vy)/self.TV;
Math.sqrt(p.vx*p.vx + p.vy*p.vy)/self.TV;
Math.sqrt(p.vx*p.vx + p.vy*p.vy)/self.TV;
This completely removed the collision lag, and lead me to believe that Math.sqrt isn't causing any problems at all. Just to be sure, though, I did this:
playSound(2, 1); //2 Is the id of the sound I want to play, and 1 is max loudness
//Math.sqrt(p.vx*p.vx + p.vy*p.vy)/self.TV;
//Math.sqrt(p.vx*p.vx + p.vy*p.vy)/self.TV;
//Math.sqrt(p.vx*p.vx + p.vy*p.vy)/self.TV;
And the lag was back! Now I'm sure that playing a sound causes problems. Am I correct? Why is this happening? How do I fix it?
I ran into this same delay issue making a sound when the player fires a weapon. My solution was two-fold:
Play each sound at load time and then pause it immediately. This will allow it to resume playing quickly, rather than playing from scratch. Do this play-pause technique after every play of the sound.
Use a pool of <audio> objects for each sound, rather than a single audio object for each sound type. Instead of just using sounds[id], use a 2D array, accessed with sound[id][count]. Here, sound[id] is a list of audio objects that all have the same sound, and count is the index of current object in use for that sound id. With each call to playSound(id), increment the count associated with that id, so that the next call invokes a different audio object.
I had to use these together, because the play-pause technique does a good job of moving the buffering delay to before the sound needs to be played, but if you need the sound rapidly, you'll still get the delay. This way, the most recently used audio object can "recharge" while another object plays.
Two things that might help you is to either utilize Web workers or to precompute several levels of loudness in advance, which you could also do in the background with worker threads. I'm saying this without going into the peculiarities of the Web audio API or how your computing the sound output, but if you've exhausted all other approaches this might be the next direction you should be focusing on.
Related
I'm recreating the children's game "Simon" on Codepen and using the Web Audio API to create tones upon a user click or upon the machine generating a sequence.
When the program starts, I initialize an audio context, then initialize four different oscillators, one for each tone/color. I also start each tone.
let context = new AudioContext();
let audioblue = context.createOscillator();
audioblue.frequency.value = 329.63;
audioblue.type = "sine";
audioblue.start();
// I repeat same code as above for audiored, audiogreen, and audioyellow
I have the program generate a random number (0, 1, 2 or 3, correlating to each quadrant of the board), one number at a time, requiring the player to recreate the full sequence before the program generates the next random number. Every time a new number is added to the sequence, I use setInterval() to "play" the sequence. "Playing" the sequence means both lighting up the quadrant and playing the corresponding tone. For playing the tone, I have the following code (the same is true for audiored, audiogreen, and audioyellow):
audioblue.connect(context.destination);
I then use setInterval() to undo the lighting up and disconnect the audio.
audioblue.disconnect(context.destination);
(There's probably a better way to do this, but as a beginner this is how I was able to figure it out.)
Everything works. The problem is you can hear an unpleasant feedback sound on the connecting and disconnecting of the tone. Is it possible to eliminate this feedback sound so the tone is more pleasant and user-friendly?
Thanks for any help you can offer.
Here's a link to the Codepen: https://codepen.io/lieberscott/pen/aEqaNd
I'm working on a web-based music sequencer/tracker, and I've noticed that in my sample playback routine, audio contexts seem to exist only for the duration of of a sample, and that the Web Audio API doesn't seem to adjust playback duration when I pitchshift a sample. For instance, if I shift a note down an octave, the routine only plays the first half of the sound before cutting off. More intense pitch downshifts result in even less of the sound playing, and while I'm not sure I can confirm this, I suspect that speeding up the audio results in relatively long periods of silence before the sound exits the buffer.
Here's my audio playback routine at the moment. So far, a lot more work has gone into making sure other functions send the right data to this than into extending the functionality of this routine.
function playSound(buffer, pitch, dspEffect, dspValue, volume) {
var source = audioEngine.createBufferSource();
source.buffer = buffer;
source.playbackRate.value = pitch;
// Volume adjustment is handled before other DSP effects are added.
var volumeAdjustment = audioEngine.createGain();
source.connect(volumeAdjustment);
// Very basic error trapping in case of bad volume input.
if(volume >= 0 && volume <= 1) {
volumeAdjustment.gain.value = volume;
} else {
volumeAdjustment.gain.value = 0.6;
}
switch(dspEffect){
case 'lowpass':
var createLowPass = audioEngine.createBiquadFilter();
volumeAdjustment.connect(createLowPass);
createLowPass.connect(audioEngine.destination);
createLowPass.type = 'lowpass';
createLowPass.frequency.value = dspValue;
break;
// There are a couple of other optional DSP effects,
// but so far they all operate in about the same fashion.
}
source.start();
}
My intent is for samples to play back fully no matter how much pitch shifting is applied, and to limit the amount of pitch shifting allowed if necessary. I've found that appending several seconds of silence to a sound works around this issue, but it's cumbersome due to the large amount of sounds I would need to process, and I'd prefer a code-based solution.
EDIT: Of the browsers I can test this in, this only appears to be an issue in Google Chrome. Samples play back fully in Firefox, Internet Explorer does not yet support the Web Audio API, and I do not have ready access to Safari or Opera. This definitely changes the nature of the help I'm looking for.
I've found that appending several seconds of silence to a sound works around this issue, but it's cumbersome due to the large amount of sounds I would need to process, and I'd prefer a code-based solution.
You could upload a sound file that is just several seconds of silence and append it to the actual audio file. Here is an SO answer that shows how to do this...
Is there a way to change the playback speed of audio in the browser? What is best to accomplish this task, html5 audio, or flash, or something else? Are there any specific libraries that would help with this?
Use the Web Audio API.
In the following code I answered your other question.
best way to loop audio in the browser?
Modify the code in my answer above as follows for a playback speed example.
Right below
source.loop = loopOnOff;
add
source.playbackRate.value = 1; // change number to between 0.10 to 10 (or larger/smaller) to test.
You can also run the html audio tag through the web audio api and add effects processing.
Interesting question there,
HTMl5 will have player speed control will have speed control..
A couple of noteworthy upcoming features are playbackRate and defaultPlaybackRate. As you can probably imagine, these fellas let us alter the speed and direction of playback. This functionality could be used for fast-forward and rewind functions or perhaps to allow users to tweak the playback speed so they can fit more podcasts into their day.
audio.playbackRate returns 1 at normal speed and acts as a multiple that is applied to the rate of playback. For example, setting playbackRate to 2 would double the speed, while setting it to -1 would play the media backwards.
audio.defaultPlaybackRate is the rate at which the audio will play after you pause and restart the media (or issue any event for that matter).
Flash Player may help( but it will be customized one you may create, with stream buffer, you need to define the player speed once buffer has the content to play.
Sound easy but will take a lot effort,
Refer VLC opesource for better Idea, its documented with ffmpeg which works with Audio,
and works with client software, in browser ti will be heavy, refer Just to have idea.
I hope this may help :)
How to create good timer for HTML5 Canvas games?
I am using RequestAnimationFrame( http://paulirish.com/2011/requestanimationframe-for-smart-animating/ )
But object's move too fast.
Something like my code is:
http://pastebin.com/bSHCTMmq
But if I press UP_ARROW player don't move one pixel, but move 5, 8, or 10 or more or less pixels. How to do if I press UP_ARROW player move 1 pixel?
Thanks for help.
The problem is that your approach of incrementing a value on each interval produces inconsistent results. Sounds like in your case RequestAnimationFrame is triggering too fast, but in other cases -- like on a slow CPU -- they would trigger slower, giving different results.
So, game animations need to be based on "absolute" time. Ie, you need to "remember" when the UP_ARROW was pressed, and where the player was at that point. Then, when you call update() determine how much time elapsed, and based on that, and the desired speed (in, say, pixels per second), move the player to the appropriate position. It makes your code more complicated, but unfortunately that's how it has to be done.
This issue is discussed a lot online. Here's one random article I found (based on Flash, but it doesn't matter):
http://www.flashgamesclassroom.com/classroom/actionscript/frame-based-vs-time-based-animation/
You can google "time based game animation" to research more.
Instead of working with 2 YouTube iFrame API players at a time (playing on one while buffering then playing on the second one and then switching and doing the same on the first one) is there a better way?
Playing videos on one and using the second one for buffering would be a lot cleaner and lot less error prone implementation if it worked - that is one player does not seem to be aware of what the other one has buffered.
All of this ends up even more important when slicing and splicing together a stream of shorter sub-sections from numerous videos because the buffering spinner ends up popping up much more often.
Any ideas ?
First you would need to use Youtube's API to get the duration of each song (then maybe put it in a database) if you haven't already.
If you want to use two players, I would suggest using Tikku's tubeplayer jquery plugin . It works wonders with youtube's jquery API. you could set a function and delay playing (and then pausing once buffer starts - also easy with Tubeplayer) after 90% of the current song has played.
that's just an idea