web audio api plays beep, beep,... beep at different rate - html5-audio

I am trying to play "beep" sound at different rate based on some sensor readings inside a browser window.
The idea is to "beep, beep, beep, ... beep" faster when the sensor reading is high, and "beep,...beep" slower when the sensor reading is low, all in real-time.
The sensor reading is fed into the browser via socket.io. I can already control a progress bar moving up and down. The audio feedback is an extra feature.
After some googling, I am thinking about using web audio api, creating a sin-wave oscillator, and to turn it on/off with a gain node connect/disconnect.
My question is how do I control the timing in the right way, say I am trying to beep at a range of frequencies from 1 Hz to 20 Hz, and be able to change the frequency dynamically.

I would most specifically NOT turn an oscillator on and off by connecting and disconnecting it - you'd have to do that from the main thread, so not super-predictable.
You can actually do this with a modulating low-frequency oscillator: check out this code:
var context = new AudioContext();
//defaults to A440Hz, sine wave
var src = context.createOscillator();
// Now let's create a modulator to turn beeps on and off
var mod = context.createOscillator();
mod.type="square";
mod.frequency.value = "2"; // Start at 2Hz
var gain = context.createGain();
var scaler = context.createGain();
src.connect(gain);
gain.connect(context.destination);
mod.connect(scaler); // Mod signal is [-1,1]
scaler.gain.value = 0.5; // we need it to be [-0.5,0.5]
gain.gain.value = 0.5; // then it's summed with 0.5, so [0,1]
scaler.connect(gain.gain);
//start it up
src.start(0);
mod.start(0);
// to change rate, change mod.frequency.value to desired frequency

Related

html5 devicemotion acceleration always changes

I develop a web application and want to use the devicemotion event to get the acceleration to measure the speed and the distance but i noticed that even the device is static on a flat surface the acceleration values on y and always change.
var clock = null, prevClock = new Date().getTime();
window.addEventListener("devicemotion", function(e) {
if (e.acceleration.x) {
clock = new Date().getTime();
var d = (clock - prevClock) / 1000;
d *= d;
motion.x = (e.acceleration.x);
motion.y = (e.acceleration.y);
motion.z = (e.acceleration.z);
distance.x += (motion.x) * d;
distance.y += (motion.y) * d;
distance.z += (motion.z) * d;
prevMotion = motion;
prevClock = new Date().getTime();
}
}, true);
how can i measure the accurate acceleration.
you are already doing it the correct way. and also you won't get an more accurate acceleration than this. this is because the sensors are so damn sensitive (or just not accurate enough) that you will NEVER get a acceleration of 0 even when your device is just lying on a flat surface. newer phones have more accurate sensors. for example an iPhone 6+ gets way closer to the 0 instead of my Galaxy S4, but even with the iPhone6+ you will never get an acceleration of 0 on a flat surface.

AS3 mp3 import error: The AMF encoding of the arguments cannot exceed 40K

NOTE: I have seen the other question in Error #2084-The AMF Encoding of the arguments cannot exceed 40K
my problem is different. My array IS NOT 0 and it is less than 40960.
My code is a simple one. I got this mp3 recording fla from this link: http://www.jordansthings.com/blog/?p=5
It uses the shinemp3 encoder.
I just wanted to play the recorded sound rather than saving it. So I added the following to the button that saves the recorded file:
private function onWavClick(e:MouseEvent)
{
// WRITE ID3 TAGS
var sba:ByteArray = mp3Encoder.mp3Data;
sba.position = sba.length - 128
sba.writeMultiByte("TAG", "iso-8859-1");
sba.writeMultiByte("Microphone Test 1-2, 1-2 "+String.fromCharCode(0), "iso-8859-1"); // Title
sba.writeMultiByte("jordansthings "+String.fromCharCode(0), "iso-8859-1"); // Artist
sba.writeMultiByte("Jordan's Thingz Bop Volume 1 "+String.fromCharCode(0), "iso-8859-1"); // Album
sba.writeMultiByte("2010" + String.fromCharCode(0), "iso-8859-1"); // Year
sba.writeMultiByte("www.jordansthings.com " + String.fromCharCode(0), "iso-8859-1");// comments
sba.writeByte(57);
//new FileReference().save(sba, "FlashMicrophoneTest.mp3") // this saves the file. I don't need it.
// my addition
var snd:Sound = new Sound();
var channel:SoundChannel = new SoundChannel();
trace(sba.length);
snd.loadCompressedDataFromByteArray(sba,sba.length);
channel = snd.play();
}
Moreover: even if this works... I cannot load an array larger than 40K???
Before calling loadCompressedDataFromByteArray, you should set the position of your ByteArray to 0. For example:
sba.position = 0;
snd.loadCompressedDataFromByteArray(sba,sba.length);
I noticed that in my application the bytesAvailable on the ByteArray was 0. This was because the position on the ByteArray was at the end of the ByteArray. The error message is confusing because it says you are exceeding 40K while you are not. It should be saying that there is nothing to load from the ByteArray.
Also, I can confirm that I can load ByteArrays that are larger than 40K. I tested with 126KB mp3 ByteArray.
In my case the problem was not the size of the ByteArray I wanted to read. I was reading and playing 30 Mb mp3 files without problem (that's a lot, I know!). The problem was that I was reading the file many times, and after the first time the position of the ByteArray was at the end. So you have to restart to 0 the position any time you want to read that byteArray. For security, I assume it is a good practice.

How can I optimise this method?

I have been working on creating an assets class that can generate dynamic TextureAtlas objects whenever I need them. The specific method is Assets.generateTextureAtlas() and I am trying to optimise it as much as possible as I quite frequently need to regenerate texture atlas's and was hoping to get a better time than my 53ms average.
53ms is currently costing me about 3 frames which can add up quickly the more items I need to pack inside my texture atlas and the frequency I need to generate them. So an answer to all the pitfalls within my code would be great.
The entire class code is available here in a github gist.
The RectanglePacker class is simply used to pack rectangles as close together as possible (similar to Texture Packer) and can be found here.
For reference, here is the method:
public static function generateTextureAtlas(folder:String):void
{
if (!_initialised) throw new Error("Assets class not initialised.");
if (_renderTextureAtlases[folder] != null)
{
(_renderTextureAtlases[folder] as TextureAtlas).dispose();
}
var i:int;
var image:Image = new Image(_blankTexture);
var itemName:String;
var itemNames:Vector.<String> = Assets.getNames(folder + "/");
var itemsTexture:RenderTexture;
var itemTexture:Texture;
var itemTextures:Vector.<Texture> = Assets.getTextures(folder + "/");
var noOfRectangles:int;
var rect:Rectangle;
var rectanglePacker:RectanglePacker = new RectanglePacker();
var texture:Texture;
noOfRectangles = itemTextures.length;
if (noOfRectangles == 0)
{
return;
}
for (i = 0; i < noOfRectangles; i++)
{
rectanglePacker.insertRectangle(Math.round(itemTextures[i].width), Math.round(itemTextures[i].height), i);
}
rectanglePacker.packRectangles();
if (rectanglePacker.rectangleCount != noOfRectangles)
{
throw new Error("Only " + rectanglePacker.rectangleCount + " out of " + noOfRectangles + " rectangles packed for folder: " + folder);
}
itemsTexture = new RenderTexture(rectanglePacker.width, rectanglePacker.height);
itemsTexture.drawBundled(function():void
{
for (i = 0; i < noOfRectangles; i++)
{
itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
rect = rectanglePacker.getRectangle(i, rect);
image.texture = itemTexture;
image.readjustSize();
image.x = rect.x + itemTexture.frame.x;
image.y = rect.y + itemTexture.frame.y;
itemsTexture.draw(image);
}
});
_renderTextureAtlases[folder] = new TextureAtlas(itemsTexture);
for (i = 0; i < noOfRectangles; i++)
{
itemName = itemNames[rectanglePacker.getRectangleId(i)];
itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
rect = rectanglePacker.getRectangle(i);
(_renderTextureAtlases[folder] as TextureAtlas).addRegion(itemName, rect, itemTexture.frame);
}
}
Well reading the project & finding what all can be optimized would sure take time.
Start by removing multiple calls to rectanglePacker.getRectangle(i) inside loops.
For example :
itemName = itemNames[rectanglePacker.getRectangleId(i)];
itemTexture = itemTextures[rectanglePacker.getRectangleId(i)];
rect = rectanglePacker.getRectangle(i);
perhaps, could have been:
rect = rectanglePacker.getRectangle(i);
itemName = itemNames[rect];
itemTexture = itemTextures[rect];
If getRectangle does indeed just 'get a rectangle' & not set anything.
I think the bigger issue at hand is this, why oh why do you HAVE to do this during run-time, in a situation when this can't take more time? This IS an expansive operation, no matter how much you optimize this you will probably end up with it taking about 40ms or similar when done in AS3.
This is why these kind of operations should be done during compile time or during "loading screens" or other "transitions" when frame-rate is not critical and when you can afford it.
Alternatively create another system in c++ or some other language which can actually handle the number-crunching that gives you the finished result.
Also, when it comes to checking performance, yes the entire function takes 53ms, BUT, where are those milliseconds used? 53ms says nothing and is only the "overhead profiling thing" where you found the culprit, you need to break it down into smaller chunks to gather some reliable information about what it is that ACTUALLY takes time, inside that function.
I mean, inside that function, you have 3 for loops, several calls to other classes, casts, deletes, creations. It's not like you are doing one thing, that function probably results in ~500 lines of code and a bazillion cpu operations. And, you have no idea where it is used. I would guess that it is the rectanglePacker.packRectangles(); that takes 60% of that time, but without profiling, you and we don't know on what to optimize, we simply don't have sufficient data.
If you HAVE to do this during run-time in AS3, I would recommend doing this spread out during several frames and distributing workload evenly during 10 frames or so. You could also doing it with help of another thread and workers. But most of all, this seems like a design error since this could probably be done at another time. And if not, then in another language which is better at these kind of operations.
The easiest way to profile this is to add a couple of timestamps similar to:
var timestamps:Array = [];
And then push getTimer() at different places in code, and then print them out when function is done
As others said, it's unlikely that the reason of bad performance is non-optimized AS code. Output from the profiler (Scout, for example) wold be very helpful. However, if your purpose is just adding new textures, I can suggest several optimizations:
Why would you need to re-generate the whole atlas every time (calling Assets.getTextures() and creating new render texture)? Why don't you just add new items to the existing atlas? Creation of a new RenderTexture (and, thus, a new texture in GPU memory) is very costly operation, because it requires sync between CPU and GPU. On the other hand, drawing into RenderTexture is carried out entirely inside GPU, so it takes much less time.
If you place every item on a grid, then you can avoid using RectanglePacker as all of your rectangles can have the same dimensions matching the dimensions of a grid.
Edit:
To clarify, some time ago I had a similar problem: I had to add new items to the existing atlas on a regular basis. And the performance of this operation was quite acceptable (about 8ms on iPad3 using 1024x1024 dynamic texture). But I used the same RenderTexture and the same Sprite object that contained my dynamic atlas items. When I need to add a new item, I just create new Image with desired texture (stand-alone or from another static atlas), then place it inside the Sprite container, and then redraw this container to the RenderTexture. Similarly with deletion/modification of an item.

How to code an audio envelope (attack time, fade in) for a Sound object?

I made a simple sine wave tone generator. The problem is that when the tone is played a strong click can be heard, and I need to implement a fast fade in (attack time) to avoid this.
I tried using tweening (like tweenmax) but it induces distortion to the audio (maybe the steps in the tweening?). I found some vague tutorials on the subject but nothing regarding attack time specifically.
How can I do this?
For the fade to sound smooth, it has to be incremented on a per-sample basis, inside your synthesis loop. A tween engine may update many times a second, but your ear can still hear the changes as a click.
In your sampleData event handler, you will have to multiply the individual samples by a volume modifier, with the range of 0 to 1, incrementing for every sample.
To quickly fade in the sound, start by setting the volume to 0, and adding a small value to it for each sample, until it reaches 1.0. You can later expand this into a more complex envelope controller.
This is a rough example of what you might start with:
for( i = 0; i < length; i++ ) {
_count++;
factor = _frequency * Math.PI * 2 / 4400;
volume += 1.0 / 4400;
if( volume > 1.0 ) volume = 1.0; //don't actually do it like this, ok?
n = Math.sin( (_count) * factor ) * volume;
_buffer.writeFloat(n);
_buffer.writeFloat(n);
}
NOTE: I haven't tested this snippet, nor would I recommend using it for production. It's just to show you roughly what I mean.
Another technique that may work for you is to put an ease / delay on the volume. Use a volumeEase variable that always 'chases' the target volume at a certain speed. This will prevent clicks when changing volumes and can be used to make longer envelopes:
var volume:Number = 0; // the target volume
var volumeEase:Number = 1.0; // the value to use in the signal math
var volumeEaseSpeed:Number = 0.001; // tweak this to control responsiveness of ease
for( i = 0; i < length; i++ ) {
_count++;
// bring the volumeEase closer to the target:
volumeEase += ( volume - volumeEase ) * volumeEaseSpeed;
factor = _frequency * Math.PI * 2 / 4400;
//use volumeEase in the maths, rather than 'volume':
n = Math.sin( (_count) * factor ) * volumeEase;
_buffer.writeFloat(n);
_buffer.writeFloat(n);
}
If you wanted to, you could just use a linear interpolation, and just go 'toward' the target at a constant speed.
Once again, the snippet is not tested, so you may have to tweak volumeEaseSpeed.

AS3 - How much time until next frame / screen draw

I have a generative art app, and I'd like it to draw as many cycles as possible each frame without reducing the framerate. Is there a way to tell how much time is left until the screen updates/refreshes?
I figure if I can approximate how many milliseconds each cycle takes, then I can run cycles until the amount of time left is less than the average or the peak cycle time, then let the screen refresh, then run another set of cycles.
If you want your app to run at N frames per second, then you can draw in a loop for 1/N seconds*, where N is typically the stage framerate (which you can get and set):
import flash.utils.getTimer;
import flash.events.Event;
private var _time_per_frame:uint;
... Somewhere in your main constructor:
stage.frameRate = 30;
_time_per_frame = 1000 / stage.frameRate;
addEventListener(Event.ENTER_FRAME, handle_enter_frame);
...
private function handle_enter_frame(e:Event):void
{
var t0:uint = getTimer();
while (getTimer()-t0 < _time_per_frame) {
// ... draw some stuff
}
}
Note that this is somewhat of a simplification, and may cause a slower resultant framerate than specified by stage.frameRate, because Flash needs some time to perform the rendering in between frames. But if you're blitting (drawing to a Bitmap on screen) as opposed to drawing in vector or adding Shapes to the screen, then I think the above should actually be pretty accurate.
If the code results in slower-than-desired framerates, you could try something as simple as only taking half the allotted time for a frame, leaving the other half for Flash to render:
_time_per_frame = 500 / stage.frameRate;
There are also FPS monitors around that you could use to monitor your framerate while drawing. Google as3 framerate monitor.
Put this code to main object and check - it will trace time between each frame start .
addEventListener(Event.ENTER_FRAME , oef);
var step:Number = 0;
var last:Number = Date.getTime();
function oef(e:Event):void{
var time:Number = Date.getTime();
step = time - last;
last = time;
trace(step);
}