HTML5 object selection and audio playback - javascript

In my software engineering course we're designing a language-learning website in HTML5 but none of us have experience with it.
The main functionality of the website is essentially playing an audio clip, having the user select an image based on what they think the audio clip corresponds to, and then playing (and internally recording) a noise based on whether they correctly identified the object.
I'm just looking for some general ideas as to how this can be done. I'm assuming the use of javascript is a given but not entirely sure about how to 'link' an image to the audio clip to determine if the user selection was correct.
Any help is appreciated and thanks a lot for taking a look!

This is a sketch: http://jsfiddle.net/pimvdb/eZWq5/.
Define a pair, and each pair can be "displayed"; it will make the audio and images appear. Clicking on an image will check whether it was the image of the same pair that the audio was from.
It does not have the noise implemented among other things you'd probably like to alter, but it might be a start.
function Pair(image, sound) {
this.image = image;
this.sound = sound;
}
Pair.prototype.display = function() {
var div1 = document.getElementById("div1");
var div2 = document.getElementById("div2");
div1.innerHTML = "";
div2.innerHTML = "";
var audio = document.createElement("audio"); // make audio
audio.src = this.sound;
audio.autoplay = true;
audio.controls = true;
div1.appendChild(audio);
var thisPair = this;
for(var i = 0; i < pairs.length; i++) { // make images
var img = document.createElement("img");
img.src = pairs[i].image;
img.onclick = function() {
if(this.src === thisPair.image) {
alert('yey');
} else {
alert('nop');
}
showRandom();
};
div2.appendChild(img);
}
};
var pairs = [
new Pair("http://lorempixel.com/100/100/?1",
"http://upload.wikimedia.org/wikipedia/"
+ "commons/a/a9/Tromboon-sample.ogg"),
new Pair("http://lorempixel.com/100/100/?2",
"http://upload.wikimedia.org/wikipedia/"
+ "commons/c/c8/Example.ogg")
];
function showRandom() {
pairs[Math.random() * pairs.length | 0].display();
}
showRandom();

To learn more about the APIs for javascript and the HTML5 audio check out Mozilla Docs.
To answer your question on how to 'link' things here is a rough sketch about how it would work. You have a list of audio clips and a list of images that goes with each and which image is correct (like a multiple choice question). Then you display the images and play the sound clip. Wait for the user to click on one of the images and then check whether it is right or not. This all can be accomplished with pretty straightforward javscript. I haven't worked with the audio API as much but you can come back with a specific question if you need more help.

Related

How to make a frequency sound stereo in JavaScript

I have some troubles while trying to reproduce different frequencies using two different audio channels (left and right) in JavaScript. I've been searching in StackOverflow and Internet for a while, but I didn't find anything that could help me, so I decided to ask here for help.
Let me explain first why I'm doing this. There's a lot of people in the world that have tinnitus (an "illness" where you hear a specific frequency in an ear or in both). Sometimes, people sat that tinnitus is not a big trouble. The website is gonna allow the users to know how a "tinnitus person" hear. For accomplishing that, the audio must be different in both ears, so I need to send different frequencies in two different channel audio.
This is the code I already have, it reproduces a specific frequency in mono (full app here: replit.com/Tupiet/hearing):
function letsStart() {
try{
window.AudioContext = window.AudioContext || window.webKitAudioContext;
context = new AudioContext();
}
catch(e) {
alert("API isn't working");
}
}
function initFrequency() {
let range = document.getElementById('range').value;
osc = context.createOscillator();
osc.frequency.value = range;
osc.connect(context.destination);
osc
osc.start(0);
document.querySelector(".show-frequency").innerHTML = range + "Hz";
}
The code above is playing a specific frequency in mono mode, but as I expected, I need to play it in a specific channel audio.
By the way, the only question I found that I thought it could help me was this one, but I think it's not what I'm searching since it doesn't work with frequencies.
How can I do it? I couldn't an explanation anywhere. Really really thanks!
You can achieve the desired result by using a ChannelMergerNode. It can be used to piece together a stereo signal.
Here is an example with two independent oscillators.
const audioContext = new AudioContext();
const leftOscillator = audioContext.createOscillator();
const leftGain = audioContext.createGain();
const rightOscillator = audioContext.createOscillator();
const rightGain = audioContext.createGain();
const merger = audioContext.createChannelMerger(2);
leftOscillator.connect(leftGain).connect(merger, 0, 0);
rightOscillator.connect(rightGain).connect(merger, 0, 1);
merger.connect(audioContext.destination);
leftOscillator.frequency.value = 800;
leftGain.gain.value = 0.5;
leftOscillator.start(0);
rightOscillator.frequency.value = 1400;
rightGain.gain.value = 0.8;
rightOscillator.start(0);

Queue getting multiple video frames

I'm creating a PDF output tool using jsPDF but need to add multiple pages, each holding a canvas image of a video frame.
I am stuck on the logic as to the best way to achieve this as I can't reconcile how to queue the operations and wait on events to achieve the best result.
To start I have a video loaded into a video tag and can get or set its seek point simply with:
video.currentTime
I also have an array of video seconds like the following:
var vidSecs = [1,9,13,25,63];
What I need to do is loop through this array, seek in the video to the seconds defined in the array, create a canvas at these seconds and then add each canvas to a PDF page.
I have a create canvas from video frame function as follows:
function capture_frame(video_ctrl, width, height){
if(width == null){
width = video_ctrl.videoWidth;
}
if(height == null){
height = video_ctrl.videoHeight;
}
canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(video_ctrl, 0, 0, width, height);
return canvas;
}
This function works fine in conjunction with the following to add an image to the PDF:
function addPdfImage(pdfObj, videoObj){
pdfObj.addPage();
pdfObj.text("Image at time point X:", 10, 20);
var vidImage = capture_frame(videoObj, null, null);
var dataURLWidth = 0;
var dataURLHeight = 0;
if(videoObj.videoWidth > pdfObj.internal.pageSize.width){
dataURLWidth = pdfObj.internal.pageSize.width;
dataURLHeight = (pdfObj.internal.pageSize.width/videoObj.videoWidth) * videoObj.videoHeight;
}else{
dataURLWidth = videoObj.videoWidth;
dataURLHeight = videoObj.videoHeight;
}
pdfObj.addImage(vidImage.toDataURL('image/jpg'), 'JPEG', 10, 50, dataURLWidth, dataURLHeight);
}
My logic confusion is how best to call these bits of code while looping through the vidSecs array as the problem is that setting the video.currentTime needs the loop to wait for the video.onseeked event to fire before code to capture the frame and add it to the PDF can be run.
I've tried the following but only get the last image as the loop has completed before the onseeked event fires and calls the frame capture code.
for(var i = 0; i < vidSecs.length; i++){
video.currentTime = vidSecs[i];
video.onseeked = function() {
addPdfImage(jsPDF_Variable, video);
};
}
Any thoughts much appreciated.
This is not a real answer but a comment, since I develop alike application and got no solution.
I am trying to extract viddeo frames from webcam live video stream and save as canvas/context, updated every 1 - 5 sec.
How to loop HTML5 webcam video + snap photo with delay and photo refresh?
I have created 2 canvases to be populated by setTimeout (5000) event and on test run I don't get 5 sec delay between canvas/contextes, sometimes, 2 5 sec. delayed contextes get populated with image at the same time.
So I am trying to implement
Draw HTML5 Video onto Canvas - Google Chrome Crash, Aw Snap
var toggle = true;
function loop() {
toggle = !toggle;
if (toggle) {
if (!v.paused) requestAnimationFrame(loop);
return;
}
/// draw video frame every 1/30 frame
ctx.drawImage(v, 0, 0);
/// loop if video is playing
if (!v.paused) requestAnimationFrame(loop);
}
to replace setInterval/setTimeout to get video and video frames properly synced
"
Use requestAnimationFrame (rAF) instead. The 20ms makes no sense. Most video runs at 30 FPS in the US (NTSC system) and at 25 FPS in Europe (PAL system). That would be 33.3ms and 40ms respectively.
"
I am afraid HTML5 provided no quality support for synced real time live video processing via canvas/ context, since HTML5 offers no proper timing since was intended to be event/s controlled and not run as real time run app code ( C, C++ ...).
My 100+ queries via search engine resulted in not a single HTML5 app I intend to develop.
What worked for me was Snap Photo from webcam video input, Click button event controlled.
If I am wrong, please correct me.
Two approaches:
create a new video element for every seek event, code provided by Chris West
reuse the video element via async/await, code provided by Adrian Wong

web audio, createMediaElementSource variable sources

lets say i wan't to have an app that has variable audio sources as audio tags like so:
<audio preload="auto" src="1.mp3" controls="" class="muzz"></audio>
<audio preload="auto" src="track.mp3" controls="" class="muzz"></audio>
Depending on which of them is played it should be passed to createMediaElementSource and then the sound would be sent to analyser and various things would be done with it, but it doesn't work:
var trackName;
//get the source of clicked track
$(".muzz").on("play", function(){
trackName = $(this).attr("src");
console.log("got a source: ", trackName);
audio = new Audio();
audio.src=trackName;
context = new AudioContext();
analyser = context.createAnalyser();
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
letsDraw();
});
the console log displays the correct source name, the letsDraw() method is supposed to draw a spectrogram of the audio playing:
function letsDraw(){
console.log("draw called");
window.requestAnimationFrame(letsDraw);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array); //get frequency from the analyser node
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle="white";
ctx.font = "bold 12px Arial";
ctx.fillText("currently playing:" + trackName, 10, 20);//this works
bars = 150;
for(var i = 0; i < analyser.frequencyBinCount; i++){ //but this doesn't
/*fill the canvas*/
x = i *2;
barWidth = 1;
barHeight = -(fbc_array[i]/1.8);
//colours react to the frequency loudness
hue = parseInt(500 * (1 - (barHeight / 200)), 10);
ctx.fillStyle = 'hsl(' + hue + ',75%,50%)';
ctx.fillRect(x, canvas.height, barWidth, barHeight);
}
}
it was working fine with one set audio source, but fails with variable sources, any ideas would be very appreciated.
of course, no errors are even thrown in the console at all.
What I don't get is why you take the src and put that in a new Audio object, as you already have them.
It is also way better to create a source from both <audio> tags. You just create a function that runs on page load (so when everything on the page is ready so you won't get any errors about elements not yet existing etc.).
Before I start writing a piece of code, what do you expect to happen? Should it be possible to have both tags playing at the same time, or should one be stopped if you click play on the other? If it shouldn't play at the same time, you'd better make one <audio> tag and create two buttons which each set the src of the tag.
Another problem with your code is that you already have the <audio> elements, and when you want them to play you just create a new audio element and append the src to it.. What is the logic behind that?
EDIT:
Here is an example of using multiple sources with only one <audio> element.
The HTML code should look like this:
<audio id="player" src="" autoplay="" controls=""></audio>
<div id="buttons">
<!--The javascript code will generate buttons with which you can play the audio-->
</div>
Then you use this JS code:
onload = function () { //this will be executed when the page is ready
window.audioFiles = ['track1.mp3', 'track2.mp3']; //this is gonna be the array with all file names
window.player = document.getElementById('player');
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
source = context.createMediaElementSource(player);
analyser = context.createAnalyser();
source.connect(analyser);
analyser.connect(context.destination);
//now we take all the files and create a button for every file
for (var x in audioFiles) {
var btn = document.createElement('button');
btn.innerHTML = audioFiles[x];
btn.onclick = function () {
player.src = audioFiles[x];
//so when the user clicks the button, the new source gets appended to the audio element
}
document.getElementById('buttons').appendChild(btn);
}
}
Hope the comments explain it good enough
EDIT2:
You want to know how to do this for multiple elements. What you want to do is to create all the audio elements when the page loads, and create all sources for it. This will decrease mess when starting to play audio. The code is here.
All you need to do is have the for loop that runs for every media file you have, which it will create an audio element for with the appropriate source, and then create a sourcenode for that (createMediaElementSource), and connect that sourcenode to the analyser.
I also want to say something about your visualiser code though. If you do not override the font, color or anything, you don't need to execute that every animationframe. Once on init is enough.
At the top of your first code block, try this:
var trackName,
context = new AudioContext();
And remove context = new AudioContext(); from the click handler.
You should only have one AudioContext for the page.

what is best to use canvas or img for frequent updates?

I have a javascript timer.
It refreshes the img src on a 200ms interval.
I have taken a look at the canvas object. I am unsure whether it is recommended to use the canvas instead of the img element?
I am running tests on both and cannot see any differences in performance.
This is my code for using the timer/img:
This is my code:
var timer4x4
var cache4x4 = new Image();
var alias = 'test';
var lastUpdate = 0;
function setImageSrc4x4(src) {
live4x4.src = src;
timer4x4 = window.setTimeout(swapImages4x4, 200);
}
function swapImages4x4() {
cache4x4.onload = function () {
setImageSrc4x4(cache4x4.src);
};
cache4x4.onerror = function () {
setImageSrc4x4("http://127.0.0.1/images/ERROR.jpg");
};
cache4x4.src = null;
cache4x4.src = 'http://127.0.0.1/Cloud/LiveXP.ashx?id=' + createGuid() + '&Alias=' + alias + '&ReSync=' + reSync;
reSync = 0;
}
*nb will add canvas code in a bit
I am streaming images from my client desktop PC to my web server. I am trying to display as many images (FPS) as possible. The image is a container for 4 smaller images. Stitched up on the client and sent to the server.
I have Googled and it says if doing pixel manipulation and aniumation use canvas.
But, I am just doing animation.
Thanks
The canvas element was designed to draw / edit / interact with images in it. If all you do is display the image, then you don't need that and a simple img is the semantically correct choice (with the added bonus of being compatible on more devices).
In both cases, the performance will be similar (if not the same) because the only thing to happen is that the image is downloaded.
While performance-wise you won't notice much of a difference, since you still cannot fully rely on HTML5 support yet, it is probably best to go with the img-solution for now.

Chrome / Firefox Cached Images Issue

I'm having trouble when preloading images and creating elements on the fly. The script loads a list of image URLs, creates canvas elements etc.
The problem is that on subsequent "soft" refreshes or submission of the same URL the preload loop isn't initiated, so the canvas elements etc. are not created. I'm pretty certain this has something to do with resource caching - however - why the script isn't iterating through the image URL list and building the DOM as expected I'm not sure... Here's an example
The JavaScript on the page is optimised output, but the preload loop looks like this:
// Images array
var images = [];
for (var i = 0; i < l.length; ++i) {
// Create canvas element
var canvas = document.createElement('canvas');
// Canvas element properties
canvas.width = l[i].w;
canvas.height = l[i].h;
canvas.style.display = 'none';
// Image element + mouse over event
images[i] = document.getElementById('i' + i).getElementsByTagName('img')[0];
images[i].addEventListener('mouseover', function() {
handleCanvas(this);
}, false);
// Push canvas into DOM
images[i].parentNode.insertBefore(canvas, images[i]);
// Preload item from processed images list
var image = new Image;
image.src = l[i].i;
}
I'd prefer not to resort to headers or "URL?random=37436464" type fixes if possible. Tested on nightly versions of Chrome and Firefox on Ubuntu 12.10.
After further investigation (testing in IE9) it appears the issue was cause by asynchronous loading of the JavaScript and lagging variable initiation.

Categories