I'm developing a Javascript music app.
Offline rendering works fine, i.e. generate a buffer, play it at a given time using an AudioBufferSourceNode. Timing is almost perfect.
But I need to generate tones that cannot be created using the default nodes of the API. So I put my sound generating logic inside the callback of a ScriptProcessorNode.
I'm fairly certain that the code in my ScriptProcessorNode is fast enough, because once started the sound plays without a glitch for any number of buffer periods that I want - so filling the buffer in time is probably not the issue here. From my experiments I figured out that the onaudioprocess event of the ScriptProcessorNode is fired at regular intervals, not depending on when the processor node was created. This creates unpredictable latency in the app: if the user presses a key right after a callback has started then it waits till the next period to play.
I've created this fiddle to demonstrate it. There is a simple instrument and two buttons to control it. One plays a pre-recorded buffer:
function playBuffer()
{
source = ac.createBufferSource();
source.buffer = buffer;
source.connect(ac.destination);
source.start(0);
};
and the other one plays the same sound but live:
function playLive()
{
processor = ac.createScriptProcessor(4096, 0, 1);
processor.onaudioprocess = function(e)
{
sineStrument.fillBuffer(e.outputBuffer.getChannelData(0), e.outputBuffer.length);
}
processor.connect(ac.destination);
};
Using the first button you can generate a rhythm and hear that it works flawlessly. Using the second button you can't because it takes around 50+ms for the sound to start.
Now notice that the instrument is really simple, I don't think I have a speed of computation issue here. Plus, if you time it right, you can get the live processed sound to play in sync with your clicks - I figure you "only" need to click just before the onaudioprocess callback is called.
The facts that 1) the playBuffer function plays immediately and 2) it is possible to get the correct timing with the playLive function tell me that there should be a technical way to get a ScriptProcessorNode timed right.
How can I do it? How come playing a buffer doesn't have fixed starting times?
I've tried reducing the buffer size of the ScriptProcessorNode too, but sound gets distorted very quickly. Why is it so? If the code in the callback wasn't fast enough wouldn't the sound have glitches after a while? It does not!
Connect your ScriptProcessorNode to a GainNode with gain value initialized to 0. Make the "Play Live" button set the gain value to 1 when pressed, 0 when released.
Related
I have a simple canvas game. It's architected with a simple requestAnimationFrame loop that updates the state, and then renders to the canvas (simple!). It runs really well on desktop + iOS, but on chrome on android, it runs terribly.
Here's the loop, for example:
var tick = function()
{
requestAnimationFrame(tick,canvas);
cur_scene.tick();
cur_scene.draw();
}
So I pulled up the remote profiler, recorded a quick session, and over (any given) 1 second, it shows ~.82s idle (.11s scripting, .04s 'other', .02 painting, .005 rendering).
It also shows ~.1s per frame (the space between consecutive Animation Frame Fired entries on the flame graph). But the flame graph is like (consistent with the other measurements) 80% empty?
So I'm at a loss- what can I do to get this to render at a higher framerate? Am I reading the diagnostic info incorrectly? Have I structured the requestAnimationFrame loop incorrectly?
Edit: Here's an annotated picture of some of the performance diagnostics (picked up by recording a remote session on my android phone)
I think you could try to put the call to requestAnimationFrame() at the end of your method like this:
var tick = function()
{
cur_scene.tick();
cur_scene.draw();
requestAnimationFrame(tick,canvas);
}
Else you might end up entering the loop again without drawing anything but requesting the animation frame again.
When you check the following example, they also first do their changes to the object before they call requestAnimationFrame():
https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
The above documentation explains this in the following text:
The method takes a callback as an argument to be invoked before the repaint. [...] You should call this method whenever you're ready to update your animation onscreen.
I have written a Javascript program that solves a puzzle game using a recessive technique.
That is, function solvePuzzle() calls function solvePuzzle() for a simpler puzzle until the solution is found. It alters the data in the board object.
I also have a function board.draw() that can display the state of the puzzle
It draws the initial game board as I expect and once I click on a button (triggering execution of solvePuzzle()) it draws the solved game board again as I expect.
However, I would like to show the intermediate puzzle states.
Initially, I inserted calls to board.draw() in the solvePuzzle() function but this does not do anything.
Researching Javascript animation has led me to create and execute this function
function animationLoop(timestamp) {
// 1 - Clear
ctx.clearRect(0, 0, c.width, c.height);
// 2 Draw
board.draw();
pieces.draw();
// call again mainloop after 16.6 ms (60 frames/s)
requestId = requestAnimationFrame(animationLoop);
}
requestId = requestAnimationFrame(animationLoop);
I am confident this is working as this only place I now call board.draw() and it show the initial state and switches to show the solved state after I press the solve button but... still no intermediate states are shown.
I then hypothesised the issue was that solution was so quick that it happens between frames but discounted this by placing this 'delay' in solvePuzzle
if (solutionCount%1000 == 0) {
confirm("Are you sure you wish to continue?");
};
I am now hypothesising solvePuzzle must run to completion before animationLoop can progress.
Is this hypothesis correct?
If so, how can I resolve my issue?
I am thinking I sort of need to continually end and resume my reclusive function at each state but cannot get my head around how I might do this.
Note: another reason I am confident the animation is working is that if I alter board from the console with say a statement like
board.layout[7].available = true;
the expected change is made to the display
JavaScript is single-threaded, and shares this thread with UI updates. Thus, when a function is started from top level, the browser does not do anything else until that function exits. This includes animation frame - animation is happening any time the page's thread is idle and an animation can be scheduled, but while your code is executing it can't.
If your calculation takes time, you need to split it into discrete pieces and let the browser breathe in between if you want UI updated (normally using setTimeout(f, 0), or inside requestAnimationFrame handler).
Another possibility is using Web Workers. They are a way to launch JavaScript in a separate thread. However, they cannot interact with the DOM at all, and can only communicate with messages. So, you can launch your calculation in a Web Worker, then from the worker periodically send messages to your main JS code in order to make it update the DOM in accordance to the results (both interim and final).
Thanks Alexander O'Mara and Kaiido for making me cover cases I forget.
I have this in page:
<video src="blob://some-live-stream" autoplay></video>
<div id="hideMePlease"> hide 1 sec before video ends</div>
I would like to hide the div 1 sec before the video ends, how can i do?
N.B: i can't know video duration, it's a live stream , and the video autostops so i have no way to stop it myself.
If, as you state, you cannot know the length of the video because it's streaming, it will be impossible (relativistic time travel notwithstanding) for you to schedule an event one second before it finishes.
However, I would at least first try to use the duration property of the video, it may be that metadata is made available as part of the stream early on. If it is, you can use that to schedule the hiding of your div.
As an aside, if you visit the page http://www.w3.org/2010/05/video/mediaevents.html, you'll find that the duration is set correctly as soon as you start playing the video, despite the fact it seems to be streaming from either an MP4, OGG or WEBM file). So it's at least possible, though it may depend on the data stream itself (whether the metadata comes before or after the actual video data).
If the data is not available (I think you get Inf for the duration in that case), then you're just going to have to hide the div at the earliest possible time after that.
That would presumably be when it finishes (fires the onended event).
So, in short, if you can get the duration (or periodically get the time remaining which might be better), use that. Otherwise fall back to hiding it at the end and start hassling w3c to provide the functionality you want in HTML6.
I am trying to create a simple 3 image slideshow with a next and back button in Flash CC using HTML5 Canvas. I'm new to javascript and seem to be having an issue with it working.
this.stop();
this.next_btn.addEventListener("click", playClickNext.bind(this));
function playClickNext()
{
this.gotoAndStop(this.currentFrame + 1);
}
this.back_btn.addEventListener("click", playClickBack.bind(this));
function playClickBack()
{
this.gotoAndStop(this.currentFrame - 1);
}
I'm getting it to publish and the next button works but sometimes goes to the wrong frame. The back button sometimes work and sometimes doesn't. The most common thing it does is also go back to a random frame when clicked.
Thanks for any help!
I put together a quick sample, and got a similar result. The issue for me was that the frame that the script was on would fire any time you went to that frame. This meant that the listeners on the button would pile up, and fire multiple times.
The ideal solution is to pull the code out of the FLA, and into your HTML/JS app. You can target the timeline directly using the instance names. For example, in my app, it is all on the main timeline, so you can use:
exportRoot.next_btn.addEventListener("click", handler);
To solve it without rearchitecting, you could also just ensure that either:
Your frame with the script is never navigated to. You could make your "first" frame 2 frames long, and put the code on frame 1, and the stop() on frame 2. Then just ensure you never gotoAndStop on 1. You will have to put restrictions on both previous and next, because you can gotoAndStop at a higher frame than the max, and it will wrap.
Remove all the event listeners each frame. The way you set up your listeners using bind is problematic for removal, so I recommend just removing all listeners.
I uploaded a quick sample here [ https://dl.dropboxusercontent.com/u/2224806/Nav.fla ] that uses the second approach, mainly because it is an easy fix.
Sorry, link expired
Let me know if this solves your issue, or if it is related to something else.
I don't know how this happens and I can't see any errors.
I can't seem to navigate through the video the second time I open my page.
See screenshot here:
I have found this error it says,
TypeError: Floating-point value is not finite.
"Video is not ready. (Video.js)"
Help would be really appreciated.
Thanks
When you say "I can't seem to navigate through the video the second time I open my page"? Do you mean you aren't able to play the video at all or that you aren't able to fast-forward and rewind within the playing video?
That you are getting a Type Error: Floating-point value is not finite error means that a certain parameter you're supplying to video.js is of the wrong type. I.e. you probaby supply video.js with a string when it wants an integer (or something similar).
Because it works the first time you load the page, are you trying to resume playback where you left off when you navigated away from the page?
If that's the case you are probably storing the currentTime parameter in a cookie or localStorage value as a string (using jQuery cookies for example these usually get automaticalyl stringified) and forgetting to switch it back to an int when you need video.js to read it back to you. Because what I notice about your screenshot is it seems video.js doesn't know the duration of your video (i.e. the seek time says 0:31 / 0:00)
Here's what you should do:
Clear you cache to get a working first time player load then:
Before starting play back, after playback has finished, and during playback you should log the current playback time signature, i.e.: console.log( videojs("id-of-your-video").currentTime() )
Adding time signature console.logs() to these video.js event callbacks should help you:
durationchange (fired if the total duration of your video changes)
duration (tells you the duration of your video. Try logging this while it works and again after it stops working and see what's changed in the value)
If you're still having trouble try logging values using the video js timeupdate callback. This event is called when the current playback position has changed (can be several times a second). If all else fails this callback might give you some insight into the exact moment you're getting the invalid type value, but it won't help you if you're problems are with trying to resume playback from a .currentTime() value reading from an incorrect type stored in user cookie / sessionStorage / localStorage etc.
Are you Using a Server to execute it or are you running it locally. Because I got some similar issues when I ran it locally. But When I put the files in some server like tomcat or iis or whatever servers it worked. Not sure about the issue just a guess