I'm creating audio record app using navigator.mediaDevices.getUserMedia() and it records every sound around me even very quiet and which is 10m away from me. I DO NOT play this sound, I only visualize it depending on volume, so I need only quite loud sounds or which are close to mic, cause there's too much interference.
Also if I enable playback to hear my mic input and start making quiet noise like tapping on the table, I can't here this sound in playback but I see it in visualizer and this is exactly what I don't want
Here's my code:
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
this.audioContext = new AudioContext();
this.sourceNode = this.audioContext.createMediaStreamSource(stream);
this.analyserNode = this.audioContext.createAnalyser();
this.sourceNode.connect(this.analyserNode);
const data = new Float32Array(this.analyserNode.fftSize);
this.analyserNode.getFloatTimeDomainData(data);
So how can I lower mic sensivity using Web Audio API or lower mic input volume or maybe transform data from analyser? I've read about AudioContext.createGain(), gain.volume, but it's used for output audio volume, not input one
I've read about AudioContext.createGain(), gain.volume, but it's used for output audio volume, not input one
No, it is used to control the volume of the audio that goes through it.
You have to see your audio context nodes as a chain, then you might understand that you can indeed use a GainNode to control the input volume of the next node to which it is connected.
Like e.g if we declare something like
gainNode.gain.volume = 0.5;
input.connect(gainNode);
gainNode.connect(analyserNode);
input.connect(audioContext.destination);
it can be seen as
Input [mic] ===> GainNode ===> AnalyserNode
100% || 50% 50%
||
===> AudioContext Output
100%
So your gainNode here did lower the volume of your AnalyserNode, but not the one of the context output.
But this is not really what you want.
Indeed, the AnalyserNode API has minDecibels and maxDecibels properties which will do exactly what you want (filter out out of db range sounds).
But these properties make sense only for frequency data (getXXXFrequencyData) since waveform doesn't take volume into account.
However, it is still possible to check if this frequency data is in our required bounds before deciding if we should draw our waveform or not.
polyfill();
(async() => {
const ctx = new AudioContext();
const input = await loadFileAsBufferNode(ctx);
const analyser = ctx.createAnalyser();
analyser.minDecibels = -90;
analyser.maxDecibels = -10;
analyser.fftSize = 512;
input.connect(analyser);
const gainNode = ctx.createGain();
input.connect(gainNode);
const bufferLength = analyser.frequencyBinCount;
const freqArray = new Uint8Array(bufferLength);
const waveArray = new Uint8Array(bufferLength);
const canvasCtx = canvas.getContext('2d');
const WIDTH = canvas.width;
const HEIGHT = canvas.height;
canvasCtx.lineWidth = 2;
draw();
// taken from https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode/maxDecibels#Example
function draw() {
requestAnimationFrame(draw);
canvasCtx.clearRect(0, 0, WIDTH, HEIGHT);
analyser.getByteFrequencyData(freqArray);
gainNode.gain.value = 1;
analyser.getByteTimeDomainData(waveArray);
var barWidth = (WIDTH / bufferLength) * 2.5;
var barHeight;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
barHeight = freqArray[i];
canvasCtx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)';
canvasCtx.fillRect(x, HEIGHT - barHeight / 2, barWidth, barHeight / 2);
x += barWidth + 1;
}
// here we check if the volume is in bounds
if (freqArray.some(isTooHigh) || !freqArray.some(hasValue)) {
canvasCtx.fillRect(0, HEIGHT / 2, WIDTH, 1);
gainNode.gain.value = 0;
return;
}
canvasCtx.beginPath();
var sliceWidth = WIDTH * 1.0 / bufferLength;
var x = 0;
for (var i = 0; i < bufferLength; i++) {
var v = waveArray[i] / 128.0;
var y = v * HEIGHT / 2;
if (i === 0) {
canvasCtx.moveTo(x, y);
} else {
canvasCtx.lineTo(x, y);
}
x += sliceWidth;
}
canvasCtx.lineTo(canvas.width, canvas.height / 2);
canvasCtx.stroke();
};
function isTooHigh(val) {
return val === 255;
}
function hasValue(val) {
return val;
}
// DOM
maxDB.oninput = e => {
const max = +maxDB.value;
if (+minDB.value >= max) minDB.value = analyser.minDecibels = max - 1;
analyser.maxDecibels = max;
}
minDB.oninput = e => {
const min = +minDB.value;
if (+maxDB.value <= min) maxDB.value = analyser.maxDecibels = min + 1;
analyser.minDecibels = min;
}
out.onchange = e => {
if (out.checked)
gainNode.connect(ctx.destination);
else
gainNode.disconnect(ctx.destination);
};
})();
function loadFileAsBufferNode(ctx, url = 'https://dl.dropboxusercontent.com/s/8c9m92u1euqnkaz/GershwinWhiteman-RhapsodyInBluePart1.mp3') {
return fetch(url)
.then(r => r.arrayBuffer())
.then(buf => ctx.decodeAudioData(buf))
.then(bufferNode => {
const source = ctx.createBufferSource();
source.buffer = bufferNode;
source.repeat = true;
source.start(0);
return source;
});
};
/* for Safari */
function polyfill() {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
try {
const prom = new AudioContext().decodeAudioData(new ArrayBuffer()).catch(e => {});
} catch (e) {
const prev = AudioContext.prototype.decodeAudioData;
Object.defineProperty(AudioContext.prototype, 'decodeAudioData', {
get: () => asPromise
});
function asPromise(audioBuffer, done, failed) {
return new Promise((res, rej) => {
prev.apply(this, [audioBuffer, onsuccess, onerror]);
function onsuccess(buf) {
if (typeof done === 'function') done(buf);
res(buf);
}
function onerror(err) {
if (typeof failed === 'function') failed(err);
rej(err);
}
});
}
}
}
<label>min<input type="range" id="minDB" min="-100" max="-1" value="-90"></label>
<label>max<input type="range" id="maxDB" min="-99" max="0" value="-10"></label>
<label>output audio<input type="checkbox" id="out"></label>
<canvas id="canvas"></canvas>
I want to show an image in the tablet of Pepper from his camera. I have done the Javascript code to process and show the image in a web view. The problem is that the image showed is strange:
I have written this code based on this example:
var CAMERA_ID = 'fger';
QiSession(function (session) {
session.service('ALMemory').then(function (ALMemory) {
// Listener to Choregraphe APP
ALMemory.subscriber('PepperQiMessaging/totablet').then(function(subscriber) {
subscriber.signal.connect(toTabletHandler);
});
// Says to Choregraphe that I'm listening with the id QR
ALMemory.raiseEvent('PepperQiMessaging/fromTabletResponse', 'QR');
// Video receiver function
session.service('ALVideoDevice').then(playVideo);
});
});
// This is the important part
function playVideo(video) {
video.subscribeCamera(
CAMERA_ID,
0, // cameraId kTop kBottom kInfred(color:17)
1, // Image of 640*480px
11, //RGB
10 // framerate 1~30
).then(function(result){
video.getImageRemote(result).then(function(image) {
if (image) {
var canvas = $('canvas')[0].getContext('2d');
var width = image[0];
var height = image[1];
var nb = image[2];
var imageData = image[6];
var read = 0;
var img = canvas.createImageData(width, height);
var pixs = img.data;
var binary = window.btoa(imageData);
var len = imageData.length;
var m = len/nb;
// Transformations to get the image in RGB
for (var i = 0; i < m; i++) {
pixs[i*4] = binary.charCodeAt(i*nb);
pixs[i*4+1] = binary.charCodeAt(i*nb+1);
pixs[i*4+2] = binary.charCodeAt(i*nb+2);
pixs[i*4+3] = 255;
}
canvas.putImageData(img, 0, 0);
}
video.unsubscribe(CAMERA_ID);
});
});
}
What I have to do to solve this problem?
At the end I found the solution. I found another example that it worked for me (the image is greyscale).
This is the algorithm to transform the bytes received form the camera to an image:
function getImage(ALVideoDevice, subscriberId) {
ALVideoDevice.getImageRemote(subscriberId).then(function (image) {
if(image) {
var imageWidth = image[0];
var imageHeight = image[1];
var imageBuf = image[6];
if (!context) {
context = document.getElementById('canvas').getContext('2d');
}
if (!imgData || imageWidth != imgData.width || imageHeight != imgData.height) {
imgData = context.createImageData(imageWidth, imageHeight);
}
var data = imgData.data;
for (var i = 0, len = imageHeight * imageWidth; i < len; i++) {
var v = imageBuf[i];
data[i * 4 + 0] = v;
data[i * 4 + 1] = v;
data[i * 4 + 2] = v;
data[i * 4 + 3] = 255;
}
context.putImageData(imgData, 0, 0);
}
if(previewRunning) {
setTimeout(function() { getImage(ALVideoDevice, subscriberId) }, 100);
}
});
}
Here is the complete Choregraphe project and the complete code.
I have a canvas animation that has been created with createjs. The entire animation script including init() function is loaded via jquery: $.getScript() on page load.
The init() and handlecomplete() function included below is then run which attaches the animation to a html canvas element on the page.
var canvas, stage, exportRoot, audio;
var tweens = [];
function init() {
canvas = document.getElementById("canvas");
images = images||{};
if (stage) {
stage.enableDOMEvents(false);
stage.removeAllChildren();
createjs.Ticker.removeAllEventListeners()
stage.enableDOMEvents(true);
}
if (audio ) {
audio.stop();
}
removeTweens();
exportRoot = null;
audio = null;
stage = null;
var loader = new createjs.LoadQueue(false);
loader.installPlugin(createjs.Sound);
loader.addEventListener("fileload", handleFileLoad);
loader.addEventListener("complete", handleComplete);
loader.loadManifest(lib.properties.manifest);
}
function handleComplete() {
exportRoot = new lib.animation2();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
stage.canvas.width = 1280;
stage.canvas.height = 720;
resizeToFit();
stage.update();
createjs.Ticker.setFPS(lib.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
createjs.Ticker.addEventListener("tick", updateTimer);
if (lib.properties.audiovolume) {
audio = createjs.Sound.play("audio", createjs.Sound.INTERRUPT_EARLY, 0, 0, -1, lib.properties.audiovolume);
}
exportRoot.gotoAndPlay(startFrame );
}
My issue is when the user makes a change, we load the script a second time using the same jquery method which returns the updated script. The init() function then executes properly and the new animation plays correctly, but our animated text (using the animateText below) does not appear on the canvas. This function is also loaded dynamically with other functions.
Checking the tween arrays, they are being created and removed as required, but they are not visible.
They are either layered behind the new animation, or not being attached to the new canvas or something else?
Simply refreshing the page will then load the new script and text properly. So clearly something in the dynamic loading of the script?
var animateText = function(localString, startX, startY, letterClip, endObject, font, color) {
var waitAmount = 0;
var offSetAmount = 20;
for(var i = 0; i < localString.length; i++){
var fl_MyInstance = new letterClip();
fl_MyInstance.localName.text = localString[i];
if(font != null){
fl_MyInstance.localName.font = font;
}
if(color != null){
fl_MyInstance.localName.color = color;
}
var localX = startX;
var localY = startY;
fl_MyInstance.x = startX + offSetAmount;
var beginX = startX + offSetAmount
offSetAmount = offSetAmount - 4
fl_MyInstance.y = startY;
fl_MyInstance.alpha = 0;
fl_MyInstance.scaleX = 0.1;
fl_MyInstance.scaleY = 0.1;
var bounds = fl_MyInstance.getBounds();
startX += bounds.width + 0;
var target = fl_MyInstance;
var tween = createjs.Tween.get(target, {
loop: false
}).wait(waitAmount)
.to({
x: localX,
y: localY,
alpha: 1,
scaleX: 1,
scaleY: 1
}, 400, createjs.Ease.circOut);
tween.waitAmount = waitAmount;
if(endObject == null){
tween.endObject = {
x: localX,
y: localY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}
} else {
tween.endObject = {
x: localX - endObject.x,
y: localY - endObject.y,
alpha: endObject.alpha,
scaleX: endObject.scaleX,
scaleY: endObject.scaleY
}
}
tween.targetClip = fl_MyInstance;
tween.arrayIndex = tweens.length;
tweens.push(tween);
waitAmount += 20;
stage.addChild(fl_MyInstance);
}
}
var removeTweens = function(){
for(var i = 0; i<tweens.length; i++){
if(tweens[i] != null){
var tween = tweens[i];
stage.removeChild(tween.targetClip);
tweens[tween.arrayIndex] = null;
}
}
}
var closeTweens = function(){
for(var i = 0; i<tweens.length; i++){
if(tweens[i] != null){
var tween = tweens[i];
createjs.Tween.get(tween.targetClip, {
loop: false
}).wait(tween.waitAmount).to(tween.endObject, 400, createjs.Ease.circOut).call(function(){
stage.removeChild(tween.targetClip);
tweens[tween.arrayIndex] = null;
});
}
}
}
var finalTweens = function(){
for(var i = 0; i<tweens.length; i++){
if(tweens[i] != null){
var tween = tweens[i];
createjs.Tween.get(tween.targetClip, {
loop: false
}).to(tween.endObject, 400, createjs.Ease.circOut);
}
}
}
Since the rest of the animation works perfectly using this method of dynamic loading, I don't think it is something in the loading. But there must be something missing in animateText and reloading functions that causes the issue.
This is the code I am using. (code is at way bottom of this post but here is link to GitHubGist :: Noitidart / _ff-addon-snippet-browseForBadgeThenCreateSaveAnApply.js) It is copy pastatble to scratchpad (i tried fiddle but it needs privelage scope). When you run it will ask you to select a 16x16 image. Then it will take the firefox icon and put it on a canvas and then take the icon you browsed to and overlay it on the bottom right. Then it will convert it to .ico and save to your desktop as profilist16.ico and profilist32.ico. It will then change the icons of all your firefox windows.
After you do the above, please open a new firefox window and then in alt+tab you'll see the firefox logo of the badged icon is dirtier.
On the bottom you see the original canvas drawing (it looks blurry but i think thats my zoom level on firefox). The icon is crisp but if you notice the badged icon (on right) on the edges (especially top) you see dirt, like black jagged stuff which is not seen in the usual icon (at left)
var win = Services.wm.getMostRecentWindow(null);
var me = win;
//these should be global vars
var sizes = []; //os dependent
var img = {}; //holds Image for each size image
var osIconFileType = 'ico'; //os dependent
var cOS = 'Windows';
function badgeIt() {
var fp = Cc["#mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
fp.init(win, "Select Badge Image", Ci.nsIFilePicker.modeOpen);
var fpCallback = function(rv) {
if (rv == Ci.nsIFilePicker.returnOK || rv == Ci.nsIFilePicker.returnReplace) {
if (sizes.length == 0) {
//figure out what os this is and populate sizes withthe sizes needed for this os
sizes = [32, 16]; //note: ask on SO how to determine what sizes the os uses for its icons?
}
loadBadgeImage();
} else {
//user did not select an file to badge with
}
}
var ranOnce0 = false;
var checkAllDefaultImagesLoaded = function() {
for (var i=0; i<sizes.length; i++) {
//console.log('img.sizes[i].loaded for i = ' + sizes[i] + ' is == ' + uneval(img[sizes[i]]));
if (!img[sizes[i]] || !img[sizes[i]].loaded) {
console.log('returning false as sizes[i]', sizes[i], 'is not loaded yet')
return false; //return as not yet all are done
}
//me.alert('all img sizes loaded');
}
//ok all sizes loaded
if (ranOnce0) {
alert('already ranOnce0 so return false');
return false;
}
ranOnce0 = true;
return true;
}
var loadDefaultImages = function() {
for (var i=0; i<sizes.length; i++) {
img[sizes[i]] = {};
img[sizes[i]].Image = new Image();
img[sizes[i]].Image.onload = function(iBinded) {
console.log('i', iBinded);
//console.log('img', img);
console.log('sizes[i]', sizes[iBinded]);
console.log('img[sizes[iBinded]].loaded=', uneval(img[sizes[iBinded]]), 'will now set it to true')
img[sizes[iBinded]].loaded = true;
console.log('just loaded size of (sizes[iBinded]) = ' + sizes[iBinded]);
var allLoaded = checkAllDefaultImagesLoaded();
if (allLoaded == true) {
console.log('allLoaded == true so createAndSave')
createAndSaveIcons();
} else {
console.warn('allLoaded is false so dont create')
}
}.bind(null, i)
img[sizes[i]].Image.src = 'chrome://branding/content/icon' + sizes[i] + '.png';
}
}
var loadBadgeImage = function() {
console.log('loadBadgeImage')
img.badge = {};
img.badge.Image = new Image();
img.badge.Image.onload = function() {
console.log('bagde image loaded')
img.badge.loaded = true;
if (checkAllDefaultImagesLoaded()) {
console.log('all dfault images PRELOADED so continue to createAndSaveIcons')
createAndSaveIcons();
} else {
console.log('all default images not loaded so start loading them')
loadDefaultImages();
}
}
img.badge.Image.src = Services.io.newFileURI(fp.file).spec;
}
var badgedIconMade = {};
var ranOnce = false;
var checkAllBadgedIconsMade = function() {
for (var i=0; i<sizes.length; i++) {
if (!badgedIconMade[sizes[i]]) {
return; //not yt done making
}
}
if (ranOnce) {
alert('already ranOnce so return');
return;
}
ranOnce = true;
// all badged icons made
applyIcons();
}
var blobCallback = function(size) {
return function (b) {
var r = new FileReader();
r.onloadend = function () {
// r.result contains the ArrayBuffer.
//alert(r.result)
img[size].ArrayBuffer = r.result;
badgedIconMade[size] = true;
//checkAllBadgedIconsMade();
Cu.import('resource://gre/modules/osfile.jsm');
var writePath = OS.Path.join(OS.Constants.Path.desktopDir, 'profilist' + size + '.' + osIconFileType);
console.log('writePath', writePath)
var promise = OS.File.writeAtomic(writePath, new Uint8Array(r.result), {tmpPath:writePath + '.tmp'});
promise.then(
function() {
//win.alert('success')
checkAllBadgedIconsMade();
},
function() {
//win.alert('failure')
}
);
};
//var url = window.URL.createObjectURL(b)
//img[size].blobUrl = url;
//prompt('', url)
r.readAsArrayBuffer(b);
}
}
var createAndSaveIcons = function() {
console.log('createAndSave')
var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
var ctx = canvas.getContext('2d');
gBrowser.contentDocument.documentElement.appendChild(canvas);
var badgeDim = { //holds key which is size of default icon, and the value is the dimension to draw the badge for that default icon size //this is set by me the dev, maybe make preference for this for user
'16': 10,
'32': 16
};
for (var i=0; i<sizes.length; i++) {
canvas.width = sizes[i];
canvas.height = sizes[i];
ctx.clearRect(0, 0, sizes[i], sizes[i]);
ctx.drawImage(img[sizes[i]].Image, 0, 0);
if (sizes[i] in badgeDim) {
if (badgeDim[sizes[i]] != sizes[i]) { //before i had `img.badge.Image.width` in place of `sizes[i]`, but can just use sizes[i] because thats the dim of the default icon duh
ctx.drawImage(img.badge.Image, sizes[i]-badgeDim[sizes[i]], sizes[i]-badgeDim[sizes[i]], badgeDim[sizes[i]], badgeDim[sizes[i]]);
} else {
//the redim size is same as icon size anyways so just draw it
ctx.drawImage(img.badge.Image, sizes[i]-badgeDim[sizes[i]], sizes[i]-badgeDim[sizes[i]]);
}
} else {
//sizes[i] is not in badgeDim meaning i dont care what size the badge is on this size of icon
ctx.drawImage(img.badge.Image, sizes[i]-badgeDim[sizes[i]], sizes[i]-badgeDim[sizes[i]]);
}
//canvas.mozFetchAsStream(mfasCallback(sizes[i]), 'image/vnd.microsoft.icon')
canvas.toBlob(blobCallback(sizes[i]), "image/vnd.microsoft.icon", "-moz-parse-options:format=bmp;bpp=32");
}
}
var applyIcons = function() {
if (cOS == 'Windows') {
Cu.import('resource://gre/modules/ctypes.jsm');
var user32 = ctypes.open('user32.dll');
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms644950%28v=vs.85%29.aspx
* LRESULT WINAPI SendMessage(
* __in HWND hWnd,
* __in UINT Msg,
* __in WPARAM wParam,
* __in LPARAM lParam
* );
*/
var SendMessage = user32.declare('SendMessageW', ctypes.winapi_abi, ctypes.uintptr_t,
ctypes.voidptr_t,
ctypes.unsigned_int,
ctypes.int32_t,
ctypes.voidptr_t
);
/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms648045%28v=vs.85%29.aspx
* HANDLE WINAPI LoadImage(
* __in_opt_ HINSTANCE hinst,
* __in_ LPCTSTR lpszName,
* __in_ UINT uType,
* __in_ int cxDesired,
* __in_ int cyDesired,
* __in_ UINT fuLoad
* );
*/
var LoadImage = user32.declare('LoadImageA', ctypes.winapi_abi, ctypes.voidptr_t,
ctypes.voidptr_t,
ctypes.char.ptr,
ctypes.unsigned_int,
ctypes.int,
ctypes.int,
ctypes.unsigned_int
);
var IMAGE_BITMAP = 0;
var IMAGE_ICON = 1;
var LR_LOADFROMFILE = 16;
var DOMWindows = Services.wm.getEnumerator(null);
while (DOMWindows.hasMoreElements()) {
var aDOMWindow = DOMWindows.getNext();
var baseWindow = aDOMWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.treeOwner
.QueryInterface(Ci.nsIInterfaceRequestor)
.nsIBaseWindow;
var nativeHandle = baseWindow.nativeHandle;
var targetWindow_handle = ctypes.voidptr_t(ctypes.UInt64(nativeHandle));
console.log('aappplying now')
var hIconBig = LoadImage(targetWindow_handle, OS.Path.join(OS.Constants.Path.desktopDir, 'profilist32.' + osIconFileType), IMAGE_ICON, 32, 32, LR_LOADFROMFILE); //MUST BE A FILEPATH TO A ICO!!!
var hIconSmall = LoadImage(targetWindow_handle, OS.Path.join(OS.Constants.Path.desktopDir, 'profilist16.' + osIconFileType), IMAGE_ICON, 16, 16, LR_LOADFROMFILE); //MUST BE A FILEPATH TO A ICO!!!
var successSmall = SendMessage(targetWindow_handle, 0x0080 /** WM_SETICON **/ , 0 /** ICON_SMALL **/ , hIconSmall); //if it was success it will return 0? im not sure. on first time running it, and it was succesful it returns 0 for some reason
var successBig = SendMessage(targetWindow_handle, 0x0080 /** WM_SETICON **/ , 1 /** ICON_BIG **/ , hIconBig); //if it was success it will return 0? im not sure. on first time running it, and it was succesful it returns 0 for some reason
}
user32.close();
}
}
fp.open(fpCallback);
}
badgeIt();
Alright. This is actually quite reproducible, but only when using BMP icons, but not PNG icons.
Seems the icon encoder that Firefox ships is pretty bad/buggy indeed (for RGBA stuff). Well, actually it is the BMP encoder that the ICO encoder uses...
So since Belgium/Algeria (the game, football, not American) was mostly boring just now, I wrote my own icon encoder, which isn't too hard actually.
So here is my complete example code incl. icon encoder (just setting the 32x32 icon), but which lacks deposing of icons. But as a bonus, it shows how to set the icon via the WNDCLASS.
Cu.import('resource://gre/modules/ctypes.jsm');
Cu.import('resource://gre/modules/osfile.jsm');
let IMAGE_BITMAP = 0;
let IMAGE_ICON = 1;
let WM_SETICON = 128;
let GCLP_HICON = -14;
let user32 = ctypes.open('user32.dll');
let SendMessage = user32.declare(
'SendMessageW',
ctypes.winapi_abi,
ctypes.intptr_t,
ctypes.voidptr_t, // HWND
ctypes.uint32_t, // MSG
ctypes.uintptr_t, // WPARAM
ctypes.intptr_t // LPARAM
);
let CreateIconFromResourceEx = user32.declare(
'CreateIconFromResourceEx',
ctypes.winapi_abi,
ctypes.voidptr_t,
ctypes.uint8_t.ptr, // icon
ctypes.uint32_t, // size
ctypes.int32_t, // icon
ctypes.uint32_t, // dwVersion
ctypes.int, // dx
ctypes.int, // dy
ctypes.uint32_t // flags
);
let SetClassLongPtr = user32.declare(
ctypes.intptr_t.size == 8 ? 'SetClassLongPtrW' : 'SetClassLongW',
ctypes.winapi_abi,
ctypes.uintptr_t,
ctypes.voidptr_t, // HWND
ctypes.int, // index
ctypes.uintptr_t // value
);
let gdi32 = ctypes.open('gdi32.dll');
let DeleteObject = gdi32.declare(
'DeleteObject',
ctypes.winapi_abi,
ctypes.int,
ctypes.voidptr_t // Object
);
let setPerWindow = false;
let badges = [
'chrome://browser/skin/places/starred48.png',
'chrome://browser/skin/places/downloads.png',
'chrome://browser/skin/places/tag.png',
'chrome://browser/skin/places/livemark-item.png',
'chrome://browser/skin/places/query.png',
'chrome://browser/skin/pluginInstall-64.png',
'chrome://browser/skin/pluginInstall-16.png',
];
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
Task.spawn(function* setIcon() {
"use strict";
try {
let p = Promise.defer();
let img = new Image();
img.onload = () => p.resolve();
img.src = 'chrome://branding/content/icon32.png';
yield p.promise;
p = Promise.defer();
let badge = new Image();
badge.onload = () => p.resolve();
badge.src = badges[getRandomInt(0, badges.length - 1)];
console.log(badge.src);
yield p.promise;
let canvas = document.createElementNS(
'http://www.w3.org/1999/xhtml',
'canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
let ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
let onethird = canvas.width / 3;
ctx.drawImage(
badge,
onethird,
onethird,
canvas.width - onethird,
canvas.height - onethird);
// Our own little ico encoder
// http://msdn.microsoft.com/en-us/library/ms997538.aspx
// Note: We would have been able to skip ICONDIR/ICONDIRENTRY,
// if we were to use CreateIconFromResourceEx only instead of also
// writing the icon to a file.
let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
let XOR = data.length;
let AND = canvas.width * canvas.height / 8;
let size = 22 /* ICONDIR + ICONDIRENTRY */ + 40 /* BITMAPHEADER */ + XOR + AND;
let buffer = new ArrayBuffer(size);
// ICONDIR
let view = new DataView(buffer);
view.setUint16(2, 1, true); // type 1
view.setUint16(4, 1, true); // count;
// ICONDIRENTRY
view = new DataView(buffer, 6);
view.setUint8(0, canvas.width % 256);
view.setUint8(1, canvas.height % 256);
view.setUint16(4, 1, true); // Planes
view.setUint16(6, 32, true); // BPP
view.setUint32(8, 40 + XOR + AND, true); // data size
view.setUint32(12, 22, true); // data start
// BITMAPHEADER
view = new DataView(buffer, 22);
view.setUint32(0, 40, true); // BITMAPHEADER size
view.setInt32(4, canvas.width, true);
view.setInt32(8, canvas.height * 2, true);
view.setUint16(12, 1, true); // Planes
view.setUint16(14, 32, true); // BPP
view.setUint32(20, XOR + AND, true); // size of data
// Reorder RGBA -> BGRA
for (let i = 0; i < XOR; i += 4) {
let temp = data[i];
data[i] = data[i + 2];
data[i + 2] = temp;
}
let ico = new Uint8Array(buffer, 22 + 40);
let stride = canvas.width * 4;
// Write bottom to top
for (let i = 0; i < canvas.height; ++i) {
let su = data.subarray(XOR - i * stride, XOR - i * stride + stride);
ico.set(su, i * stride);
}
// Write the icon to inspect later. (We don't really need to write it at all)
let writePath = OS.Path.join(OS.Constants.Path.desktopDir, 'icon32.ico');
yield OS.File.writeAtomic(writePath, new Uint8Array(buffer), {
tmpPath: writePath + '.tmp'
});
// Cut off ICONDIR/ICONDIRENTRY for CreateIconFromResourceEx
buffer = buffer.slice(22);
let hicon = CreateIconFromResourceEx(
ctypes.uint8_t.ptr(buffer),
buffer.byteLength,
IMAGE_ICON,
0x30000,
0,
0,
0);
if (hicon.isNull()) {
throw new Error("Failed to load icon");
}
if (setPerWindow) {
let DOMWindows = Services.wm.getEnumerator(null);
while (DOMWindows.hasMoreElements()) {
let win = DOMWindows.getNext().QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).
QueryInterface(Ci.nsIDocShellTreeItem).
treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIBaseWindow);
let handle = ctypes.voidptr_t(ctypes.UInt64(win.nativeHandle));
if (handle.isNull()) {
console.error("Failed to get window handle");
continue;
}
var lparam = ctypes.cast(hicon, ctypes.intptr_t);
var oldIcon = SendMessage(handle, WM_SETICON, 1, lparam);
if (ctypes.voidptr_t(oldIcon).isNull()) {
console.log("There was no old icon", oldIcon.toString());
}
else {
console.log("There was an old icon already", oldIcon.toString());
// In a perfect world, we should actually kill our old icons
// using DeleteObject...
}
}
}
else {
let win = Services.wm.getMostRecentWindow(null).
QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIWebNavigation).
QueryInterface(Ci.nsIDocShellTreeItem).
treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIBaseWindow);
let handle = ctypes.voidptr_t(ctypes.UInt64(win.nativeHandle));
if (handle.isNull()) {
throw new Error("Failed to get window handle");
}
let oldIcon = SetClassLongPtr(handle, GCLP_HICON, ctypes.cast(hicon, ctypes.uintptr_t));
if (ctypes.voidptr_t(oldIcon).isNull()) {
console.log("There was no old icon", oldIcon.toString());
}
else {
console.log("There was an old icon already", oldIcon.toString());
// In a perfect world, we should actually kill our old icons
// using DeleteObject...
}
}
console.log("done", badge.src);
}
catch (ex) {
console.error(ex);
}
});
PS: Here is a screenshot from the Task Switcher on XP:
Tested the code on Win7, the icon once applied is real real crappy. In image below, we see the icon in canvas is perfect. In the alt+tab menu the first icon is SUPER crappy, second icon is unbadged so perfect, and third and fifth icons are normal crappy.
Image is here: http://i.stack.imgur.com/47dIr.png
Edit:
Fixed the SUPER crappiness by changing this line for win7, it was 32, 32, i made it 256, 256, no clue why it fixed the SUPER crap:
var hIconBig = LoadImage(targetWindow_handle, OS.Path.join(OS.Constants.Path.desktopDir, 'profilist32.' + osIconFileType), IMAGE_ICON, 256, 256, LR_LOADFROMFILE);
before it was: var hIconBig = LoadImage(targetWindow_handle, OS.Path.join(OS.Constants.Path.desktopDir, 'profilist32.' + osIconFileType), IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
However the usual crap, black rough edge remains.
I want to set a new position of body on BeginContact event but it's still not functional. It's writed in JavaSript with drawing to canvas but it doesn't matter for Box2d. In HTML file in body is only empty canvas, nothing else. Here is my code:
In the beginning of JS file are only declarated some variables.
Vec2 = Box2D.Common.Math.b2Vec2;
BodyDef = Box2D.Dynamics.b2BodyDef;
Body = Box2D.Dynamics.b2Body;
FixtureDef = Box2D.Dynamics.b2FixtureDef;
Fixture = Box2D.Dynamics.b2Fixture;
World = Box2D.Dynamics.b2World;
PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;
DebugDraw = Box2D.Dynamics.b2DebugDraw;
var player;
It's followed by a setup function which is called in the beginning.
function setup()
{
canvas = document.getElementById("collisionCanvas");
context = canvas.getContext('2d');
document.getElementsByTagName('body')[0].style.backgroundColor = "black";
canvas.style.backgroundColor = "white";
canvas.width = 320;
canvas.height = 320;
world = new World(new Vec2(0, 10), false);
//Point of the problem!!!
//setting contact listener
var listener = new Box2D.Dynamics.b2ContactListener;
listener.BeginContact = function(contact)
{
var body1 = contact.GetFixtureA().GetBody();
var body2 = contact.GetFixtureB().GetBody();
if(body1.GetUserData().type == "player")
{
body1.SetPosition({x:5, y:5});
}
else
{
body2.SetPosition({x:5, y:5});
}
}
world.SetContactListener(listener);
var fixDef = new FixtureDef;
fixDef.density = 1.0;
fixDef.friction = 0.5;
fixDef.restitution = 0.2;
var bodyDef = new BodyDef;
//creating ground
bodyDef.type = Body.b2_staticBody;
bodyDef.position.x = convertPixelsToMeters(160);
bodyDef.position.y = convertPixelsToMeters(320-32/2);
bodyDef.userData = {type: "static"};
fixDef.shape = new PolygonShape;
fixDef.shape.SetAsBox(convertPixelsToMeters(canvas.width/2), convertPixelsToMeters(32/2));
world.CreateBody(bodyDef).CreateFixture(fixDef);
//creating player
bodyDef.type = Body.b2_dynamicBody;
bodyDef.fixedRotation = true;
bodyDef.position.x = convertPixelsToMeters(160);
bodyDef.position.y = convertPixelsToMeters(160);
bodyDef.userData = {type: "player"};
fixDef.shape = new PolygonShape;
fixDef.shape.SetAsBox(convertPixelsToMeters(16), convertPixelsToMeters(16));
player = world.CreateBody(bodyDef);
player.CreateFixture(fixDef);
//setup debug draw
var debugDraw = new DebugDraw();
debugDraw.SetSprite(document.getElementById("collisionCanvas").getContext("2d"));
debugDraw.SetDrawScale(32.0);
debugDraw.SetFillAlpha(0.3);
debugDraw.SetLineThickness(1.0);
debugDraw.SetFlags(DebugDraw.e_shapeBit | DebugDraw.e_jointBit);
world.SetDebugDraw(debugDraw);
window.setInterval(update, 1000 / 60);
}
And in the end are only update function, one helping function and that's it.
function update()
{
world.Step(
1 / 60 //frame-rate
, 10 //velocity iterations
, 10 //position iterations
);
world.DrawDebugData();
world.ClearForces();
}
function convertPixelsToMeters(x)
{
return x*0.03125;
}
$(function(){
setup();
})
Important is only the middle code where is BeginContact event where is calling the SetPosition function which doesn't work.
I tried change position in other places, for example on KeyDown event and there it was correct, so it's for me understandable why it doesn't work.
In the b2Contactlistner method we can not change any prperty or position.
You can take any boolean variable and make it true when in beign contact and if change the position of body according to boolean variable.
as in your code.......
var bodyyy;
var boolennn
listener.BeginContact = function(contact)
{
var body1 = contact.GetFixtureA().GetBody();
var body2 = contact.GetFixtureB().GetBody();
if(body1.GetUserData().type == "player")
{
//body1.SetPosition({x:5, y:5});
bodyyy = body1;
booleannn = true;
}
else
{
// body2.SetPosition({x:5, y:5});
bodyyy = body2;
boolennn = true;
}
}
Now In your Update method
if(booleann)
{
bodyyy.SetPosition({x:5, y:5})
}
SORRY I Donot know syntax of java script