Changing the pitch of the sound of an HTML5 audio node - html

I would like to change the pitch of a sound file using the HTML 5 Audio node.
I had a suggestion to use the setVelocity property and I have found this is a function of the Panner Node
I have the following code in which I have tried changing the call parameters, but with no discernible result.
Does anyone have any ideas, please?
I have the folowing code:
var gAudioContext = new AudioContext()
var gAudioBuffer;
var playAudioFile = function (gAudioBuffer) {
var panner = gAudioContext.createPanner();
gAudioContext.listener.dopplerFactor = 1000
source.connect(panner);
panner.setVelocity(0,2000,0);
panner.connect(gainNode);
gainNode.connect(gAudioContext.destination);
gainNode.gain.value = 0.5
source.start(0); // Play sound
};
var loadAudioFile = (function (url) {
var request = new XMLHttpRequest();
request.open('get', 'Sounds/English.wav', true);
request.responseType = 'arraybuffer';
request.onload = function () {
gAudioContext.decodeAudioData(request.response,
function(incomingBuffer) {
playAudioFile(incomingBuffer);
}
);
};
request.send();
}());

I'm trying to achieve something similar and I also failed at using PannerNode.setVelocity().
One working technique I found is by using the following package (example included in the README): https://www.npmjs.com/package/soundbank-pitch-shift
It is also possible with a biquad filter, available natively. See an example here: http://codepen.io/qur2/pen/emVQwW
I didn't find a simple sound to make it obvious (CORS restriction with ajax loading).
You can read more at http://chimera.labs.oreilly.com/books/1234000001552/ch04.html and https://developer.mozilla.org/en-US/docs/Web/API/BiquadFilterNode.
Hope this helps!

Related

Create ko.observableArray from JSON object in Knockout JS

I have JSON object loaded to my view model. I want to push that into an observableArray.
function viewModel()
{
var self = this;
self.details = [{"id":1,"first_name":"fname1","last_name":"lname1","salary":1000.0},
{"id":2,"first_name":"fname2","last_name":"lname2","salary":2000.0},
{"id":3,"first_name":"fname3","last_name":"lname3","salary":3000.0}];
self.emp = ko.observableArray([new Model(self.details[0]),new Model(self.details[1]),new Model(self.details[2])]);
//This method works, but is very inefficient...
}
ko.applyBindings(new viewModel());
The solution I found was to feed each element individually, which is not practical.
I'm using JQuery. I found some solutions using knockout.mapping plugin. But I'm unable to add that plugin to my Eclipse workspace correctly.
I'm new to Knockout. Please help me find a solution to add the entire object to the observableArray.
I would probably use the mapping plugin personally, but this should also work
function viewModel()
{
var self = this;
self.details = [{"id":1,"first_name":"fname1","last_name":"lname1","salary":1000.0},
{"id":2,"first_name":"fname2","last_name":"lname2","salary":2000.0},
{"id":3,"first_name":"fname3","last_name":"lname3","salary":3000.0}];
var mapped = self.details.map(m => new Model(m));
//or
//var mapped = self.details.map(function(m) {return new Model(m);});
self.emp = ko.observableArray(mapped);
}
ko.applyBindings(new viewModel());

Adding static image to Lightswitch HTML 2013 Browse Screen

In my case, I have color coded some tiles in the HTML client and I want to add a simple color code key. I have the PNG file I want to use.
I do not require the ability to upload or change the image.
This link seems to achieve what I am looking for, but I am not sure where to implement. Does all of this code go into the PostRender of the Image Control I created?
Add image to lightswitch using local property and file location
Here is what the PostRender of the simple Image data item I created as an Image Local Property, and then dragged into the Solution Designer. It was basically copied from the link above, but I did change the name of the image file to match mine, and I have already added the item to the Content\Images folder structure and it shows in the file view:
myapp.BrowseOrderLines.ColorKey_postRender = function (element, contentItem) {
// Write code here.
function GetImageProperty(operation) {
var image = new Image();
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
// XMLHttpRequest used to allow external cross-domain resources to be processed using the canvas.
// unlike crossOrigin = "Anonymous", XMLHttpRequest works in IE10 (important for LightSwitch)
// still requires the appropriate server-side ACAO header (see https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image)
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var url = URL.createObjectURL(this.response);
image.onload = function () {
URL.revokeObjectURL(url);
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
var dataURL = canvas.toDataURL("image/png");
operation.complete(dataURL.substring(dataURL.indexOf(",") + 1));
};
image.src = url;
};
xhr.open('GET', this.imageSource, true);
xhr.responseType = 'blob';
xhr.send();
};
myapp.BrowseOrderLines.ColorKey_postRender = function (element, contentItem) {
// Write code here.
msls.promiseOperation
(
$.proxy(
GetImageProperty,
{ imageSource: "content/images/Key.png" }
)
).then(
function (result) {
contentItem.screen.ImageProperty = result;
}
);
};
}
Currently, the Image control does show on the screen in the browser, and is the custom size I choose, but it is just a light blue area that does not display my image file.
I am not sure if I have embedded the image? I am not sure if that is a missing step?
Thank you!!
The easiest method of testing this approach would be to change your postRender to the following (which embeds the helper function within the postRender function):
myapp.BrowseOrderLines.ColorKey_postRender = function (element, contentItem) {
function GetImageProperty(imageSource) {
return msls.promiseOperation(
function (operation) {
var image = new Image();
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
// XMLHttpRequest used to allow external cross-domain resources to be processed using the canvas.
// unlike crossOrigin = "Anonymous", XMLHttpRequest works in IE10 (important for LightSwitch)
// still requires the appropriate server-side ACAO header (see https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image)
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var url = URL.createObjectURL(this.response);
image.onload = function () {
URL.revokeObjectURL(url);
canvas.width = image.width;
canvas.height = image.height;
ctx.drawImage(image, 0, 0);
var dataURL = canvas.toDataURL("image/png");
operation.complete(dataURL.substring(dataURL.indexOf(",") + 1));
};
image.onerror = function () {
operation.error("Image load error");
};
image.src = url;
};
xhr.open('GET', imageSource, true);
xhr.responseType = 'blob';
xhr.send();
}
);
};
GetImageProperty("content/images/Key.png").then(function onComplete(result) {
contentItem.screen.ImageProperty = result;
}, function onError(error) {
msls.showMessageBox(error);
});
};
This assumes that you named the local property ImageProperty as per my original post and the Image control on your screen is named ColorKey.
In the above example, I've also taken the opportunity to slightly simplify and improve the code from my original post. It also includes a simple error handler which may flag up if there is a problem with loading the image.
If this still doesn't work, you could test the process by changing the image source file-name to content/images/user-splash-screen.png (this png file should have been added as a matter of course when you created your LightSwitch HTML project).
As the GetImageProperty function is a helper routine, rather than embedding it within the postRender you'd normally place it within a JavaScript helper module. This will allow it to be easily reused without repeating the function's code. If you don't already have such a library and you're interested in implementing one, the following post covers some of details involved in doing this:
Lightswitch HTML global JS file to pass variable

backbone.js fetch json success will not hit

i use fetch from backbone.js to load a json model but success will not hit.
var DialogModel = Backbone.Model.extend({
url : function() {
return '/messages/getDialog';
},
parse : function(res) {
return res.dialog;
}
});
var DialogView = Backbone.View.extend({
el: $("#page"),
initialize: function() {
var onDataHandler = function() {
this.render();
};
this.model = new DialogModel();
this.model.fetch({ success : onDataHandler});
},
render: function(){
var data = {
dialogModel : this.model
};
var form = new Backbone.Form({
model: data
});
$(this.el).html(form.render().el);
}
});
What happens now:
DialogView initialize is called.
this.model.fetch is called but the onDataHandler function will not be hit if success.
/messages/getDialog throws a json file back.
The json file is loading well as i can see in the network browser.
Thanks for your help!
Oleg
The problem you're having is due to a typical JS gotcha and not related to Backbone itself. Try
var that = this;
this.model.fetch({
success : function () {
that.render();
}
});
The way you're currently passing onDataHandler is problematic as it will cause this to refer to the global object instead of the DialogView, when the function is called.
This fiddle demonstrates the problematic version vs one that works.
(You may also want to take a look at JS strict mode which can shield you from this type of errors.)
Even better is to listen for an event:
this.model.on("sync", this.render).fetch();
I ran across this question while looking for something else, but the currently accepted answer drives me nuts. There's no good reason to be sprinkling this and that all over your code. Backbone (underscore) includes a context parameter that you can bind to.
that = this makes no sense. If you must implement obsolete 2007-era Crockford patterns, then say var self = this. Saying that = this is like saying left = right. Everyone Stop.

How to get FFT from audio tag in HTML5

I want to get the FFT data from an <audio> tag, but it doesn't work without any syntax error. Looking at the Web Audio API document, I write a sample code, here is my code:
<audio id="aud" controls="controls" src="test.mp3"></audio>
<script type="text/javascript">
var audioElement = document.getElementById("aud");
var audioContext = new webkitAudioContext();
var streamingAudioSource = audioContext.createMediaElementSource(audioElement);
var jsProcessor = audioContext.createJavaScriptNode(4096,1,1);
jsProcessor.onaudioprocess = process;
var analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
//streaming:AudioSource->jsProcessor->analyser->destination
streamingAudioSource.connect(jsProcessor);
jsProcessor.connect(analyser);
analyser.connect(audioContext.destination);
//autoplay
audioElement.play();
function process(event){
var freqByteData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(freqByteData);
document.getElementById("info").innerHTML=freqByteData[1];//show data in div
}
</script>
My Chrome version is 20.0.1096.1 dev-m and I think it works well. Through process(), I'm trying to write down freqByteData, but it shows 0, and all of them are always 0.
It must have something wrong of my code, and I want to know how to get frequency data from an audio tag.
It seems that createMediaSourceElement breaks if it's called before window.onload. There is a bug report about this issue: http://code.google.com/p/chromium/issues/detail?id=112368
There are currently two workarounds:
Wait the window load event before executing the whole javascript
window.addEventListener('load', function(){
// your code
}, false);
or
Create the MediaElementSource in a setTimeout with 0 delay
setTimeout(function (){
var streamingAudioSource = audioContext.createMediaElementSource(audioElement);
var jsProcessor = audioContext.createJavaScriptNode(4096,1,1);
// The rest of the code
}, 0);

context.getImageData() on localhost?

I have the following snippet of code and I'm trying to run it from localhost (OSX, running XAMPP):
var canvas = document.getElementById('mycanvas');
var cx = canvas.getContext('2d');
var myImg = new Image();
myImg.src = 'images/lion.jpg';
$(myImg).load(function() {
cx.drawImage(myImg, 0, 0);
var imgData = cx.getImageData(0,0,150,150);
});
But when I run it I get this error from the console:
Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
site.js:11Uncaught Error: SECURITY_ERR: DOM Exception 18
I found some similar questions here and I know that this has something to do with the fact that I'm working locally and this wouldn't happen if I was trying to access the image from the same domain. I don't know if this makes sense, but it's what I understood.
My question is, how can I make this work in a local dev environment?
Serve your html with an HTTP server, for example, Apache or Nginx.
Mac OSX comes with Python installed, so you can simply start an HTTP server by opening a terminal, then input:
cd /path/to/my/work/
python -m SimpleHTTPServer
Then open http://localhost:8000/ in your browser. This should work.
Trying a different browser might help. This happened to me on Chromium and just by switching to Firefox I was able to continue debugging locally.
I ended up using a combo of solutions (see this other problem)
Steps:
Transform the image to base64 string.
Assign that as the src of the Image js object.
Draw on canvas
Code:
// helper to transform to base64
// see other question for more help
function toDataUrl(url, callback) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
callback(reader.result);
};
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.send();
}
var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var png = new Image();
var backgroundSrc = ".....";
png.onload = function () {
ctx.drawImage(png, 0, 0);
};
toDataUrl(backgroundSrc, function(base64Img) {
png.src = base64Img;
});
I know this solution is NOT optimal, but it worked for me AND it helps when the image does not come from the same origin.
I'm posting this for anyone that encounters the same problem.