Discovering the Web Audio Api I wanted to draw a waveform for sound files. Now, I am aware of the image dimension limitations in some browsers, and I tried to look them up, but they seem to be ever changing (or at least memory differences like Chrome Desktop vs Chrome Mobile).
I tried to look up how to test if an image, or a Canvas / 2D Context can be of a certain size with almost no success. However, when testing this thing in Firefox I did get an error in the console so I tried the following method:
wfWidth = source.buffer.duration*100;
document.getElementById("waveform").width = wfWidth;
wfCtx = document.getElementById("waveform").getContext("2d");
var success = false;
while(success == false)
{
try {
var temp = wfCtx.getImageData(0,0,10,10); // this produced an error further down the code, so I use this as my test case.
success = true;
} catch(err) {
wfWidth = wfWidth / 2;
document.getElementById("waveform").width = wfWidth;
wfCtx = document.getElementById("waveform").getContext("2d");
}
console.log(success);
}
This does seem to work in Firefox as the console first outputs false showing that the canvas is too big and then true after halving the width of the canvas and the waveform is shown.
However, in Google Chrome on Desktop the canvas seems to be of a certain size (as indicated by a scroll bar) but it is totally blank. When I right-click to save image, it is a 0 byte txt file. On Chrome Mobile (android) I get this little square sad face. Guess that method of checking doesn't work in Chrome.
What would be the best way to test if the canvas is, well, valid, and resize if it is not?
Related
I am using web audio API to stream audio to my remote server. I am using OfflineAudioContext. The code works fine in chrome and firefox but in safari, it gives the above mentioned error when trying to use OfflineAudioContext. I have tried adding webkit prefix to OfflineAudioContext, then it gives me this error:
SyntaxError: The string did not match the expected pattern.
I have tried adding different values to the OfflineAudioContext constructor but its always giving me the same error.
I went through Mozilla developers page for browser compatibility and I found this :
So, here its mentioned that for OfflineAudioContext constructor the compatibility is unknown for edge and safari. So, is this the reason, why I am not able to use OfflineAudioContext in safari? Is it not supported yet? or Am I doing it wrong? Or Is there another way to solve this in safari?
This is the first time I am using the Web Audio API. So, I hope somebody can clear my doubt if I missed out somewhere. Thank You.
Code of OfflineAudioContext added below:
let sourceAudioBuffer = e.inputBuffer; // directly received by the audioprocess event from the microphone in the browser
let TARGET_SAMPLE_RATE = 16000;
let OfflineAudioContext =
window.OfflineAudioContext || window.webkitOfflineAudioContext;
let offlineCtx = new OfflineAudioContext(
sourceAudioBuffer.numberOfChannels,
sourceAudioBuffer.duration *
sourceAudioBuffer.numberOfChannels *
TARGET_SAMPLE_RATE,
TARGET_SAMPLE_RATE
);
(if more code is needed of the js file to know the problem better. Just comment for it. I will add that but I thought the snippet is enough to understand the problem)
It's a bit confusing, but a SyntaxError is what Safari throws if it doesn't like the arguments. And unfortunately Safari doesn't like a wide range of arguments which should normally be supported.
As far as I know Safari only accepts a first parameter from 1 to 10. That's the parameter for numberOfChannels.
The second parameter (the length) just needs to be positive.
The sampleRate can only be a number between 44100 and 96000.
However it is possible to translate all the computations from 16kHz to another sampleRate which then works in Safari. Let's say this is the computation you would like to do at 16kHz:
const oac = new OfflineAudioContext(1, 10, 16000);
const osciallator = oac.createOscillator();
osciallator.frequency.value = 400;
osciallator.connect(oac.destination);
osciallator.start(0);
oac.startRendering()
.then((renderedBuffer) => {
console.log(renderedBuffer.sampleRate);
console.log(renderedBuffer.getChannelData(0));
});
You can do almost the same at 48kHz. Only the sampleRate will be different but the channelData of the rendered AudioBuffer will be the same.
const oac = new webkitOfflineAudioContext(1, 10, 48000);
const osciallator = oac.createOscillator();
osciallator.frequency.value = 1200;
osciallator.connect(oac.destination);
osciallator.start(0);
oac.oncomplete = (event) => {
console.log(event.renderedBuffer.sampleRate);
console.log(event.renderedBuffer.getChannelData(0));
};
oac.startRendering();
Aside: Since I'm the author of standardized-audio-context which is a library that tries to ease out inconsistencies between browser implementations, I have to mention it here. :-) It won't help with the parameter restrictions in Safari, but it will at least throw the expected error if the parameter is out of range.
Also please note that the length is independent of the numberOfChannels. If IfsourceAudioBuffer.duration in your example is the duration in seconds, then you just have to multiply it with the TARGET_SAMPLE_RATE to get the desired length.
So basically I'm trying to alter the perspective of an image by using the gyroscope in a smartphone/tablet. So far I've got most working, on anything but IOS (classic developer issue).
https://jsfiddle.net/seiftie/db020Lxk/34/
NOTE: This can only be viewed from a device with a gyroscope. If it works, you'll see a status (ex: DeviceOrientationEvent activated).
So for some reason this block doesn't work on IOS browsers:
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', doDeviceOrientation);
}
var doDeviceOrientation = function(event){
// THIS NEVER GETS TRIGGERED IN IOS
debug("Inside the doDeviceOrientation function after eventListener");
status("DeviceOrientationEvent activated");
sendCoords(event.alpha, event.beta, event.gamma);
distortImage(event.alpha, event.beta, event.gamma);
}
I don't seem to understand what I'm missing here. If my question caused confusion, let me know in the comments below and I'll try to clarify.
This is driving me crazy. Here is the code I use to set current time:
$("#audio").click(function(e) {
e.preventDefault();
mr_waveform_skip(e)
});
function mr_waveform_skip(event) {
clientX = event.clientX;
left = event.currentTarget.offsetLeft;
clickoffset = clientX - left;
percent = clickoffset/event.currentTarget.offsetWidth
audio_duration = audio_element.duration;
duration_seek = percent*audio_duration;
audio_element.currentTime = duration_seek;
// audio_element.currentTime = 10;
console.log('CLICK: ' + duration_seek + ' Element: ' + audio_element + ' CurrentTime: ' + audio_element.currentTime);
}
I cannot seem to set audio_element.currentTime only get it!
And worse, it works in fireFox! Chrome restarts at 0, no matter what.
This is what the above code produces in Firefox console:
CLICK: 63.82905432385121 Element: [object HTMLAudioElement] CurrentTime: 3.849546
And in Chrome:
CLICK: 63.82905432385121 Element: [object HTMLAudioElement] CurrentTime: 3.849546
See? The same one! We can see that Chromes sees the HTML audio element (since it can get the value). If I do audio_element.currentTime = 10; it still does not work (in Chrome, Firefox loyally restarts at 10)..
Your code works perfectly when I tested it with with an Ogg file from Wikipedia.. If that really is all of your code, then this problem appears to be caused by some kind of corruption or unexpected format in your media file.
You will not be able to fix this problem with code; you will need to produce a new media file that your browser can process correctly. Perhaps try using a different piece of audio software (or different settings) to produce or re-process the media file.
If the format isn't the problem:
I've bumped into this problem in Chrome a couple of times today. Wasted so much time trying to solve it. (Version 55.x)
The solution is to restart Chrome. After countless page refreshes the value is suddenly not set. It's probably some cache bug. I would stress that using a development server supporting range requests is also a good idea.
If I'm not running Node or Django I use : https://github.com/danvk/RangeHTTPServer
Modern browsers except IE handle MJPEG (Motion JPEG). Here is an example fiddle.
Can I detect support for MJPEG? I have looked through Modernizr in vain.
Modernizr only supports the following formats for detection right now: ogg, webm and h264.
The video element has a call called canPlayType(format) that would really be your only option (if it works for mjpg). Your detection logic would look something like this (not the format would be different).
var videoElement = document.createElement('video');
if(!!videoElement.canPlayType)
{
var browserConfidence = videoElement.canPlayType('video/mjpeg; codecs="insert, them"');
if(browserConfidence == "probably")
{
// high confidence
}
else if(browserConfidence == "maybe")
{
// low confidence
}
else
{
// no confidence... it definately will not play
}
}
Make sure you visit the W3C's information on canPlayType. It looks like the mime type should be "video/mjpeg" and not "video/mjpg" as you specified earlier.
I've tried the most obvious way to detect if the image could be loaded or not:
$output = $('<img id="webcam">')
.attr('src', src)
.load(function(){alert('ok')})
.error(function(){alert('error')});
In case image could be loaded load event will be fired, otherwise error. Checked this in recent Chrome and IE8. Works as expected.
Sadly for this you would need to use an ActiveX control to support mjpg in IE. See How to embed mjpeg file on a webpage.
I'm currently working on a WordPress addition which loads full post content (normally it shows exceprts) when asked to. I did my code like this:
$(".readMore").click(function() {
var url = $(this).attr("href");
$(this).parent("p").parent("div").children("div.text").slideUp("slow", function () {
$(this).load(url + " .text", function(){
$(this).slideDown("slow");
});
});
$(this).parent("p").fadeOut();
return false; });
And it works. But I don't want images to be loaded. I tried .text:not(img), but it didn't worked. How can I do this?
The trick, of course, is preventing the images from being downloaded unnecessarily by the user's browser; not displaying them is easy.
I only have two browsers were it's easy and convenient to tell what's downloading: Chrome and Firefox+Firebug. In my tests, Martin's solution using *:not(img) results in the images being downloaded (although not displayed) in both Chrome and Firefox+Firebug. (I emphasize "Firefox+Firebug" because Firebug can change the behavior of Firefox on occasion, and so it may well be changing its behavior here, although I don't think it is; more on that below.)
It took some tweaking, but this seems to do the trick (more on testing below):
$.ajax({
url: url,
success: function(data) {
var div = $("<div>").html(data);
if (stripImages) {
// Find the images, remove them, and explicitly
// clear the `src` property from each of them
div.find("img").remove().each(function() {
this.src = "";
});
}
$(targetSelector).append(div.children());
},
error: function(jxhr, status, err) {
display("ajax error, status = " + status + ", err = " + err);
}
});
Live example The "Include big image" checkbox includes a large file from NASA's Astronomy Picture of the Day (APOD).
The key there was setting the src of the img elements to "". On Chrome, just removing the elements was enough to prevent Chrome starting the download of the images, but on Firefox+Firebug it not only started downloading them, but continued even when the download took considerable time. Clearing the src causes Firefox to abort the download (I can see this in the Firebug Net console).
So what about IE? Or Firefox without Firebug? I only did unscientific testing of those, but it's promising: If I run my live example of Martin's solution on either IE or Firefox without Firebug in a VM, I see the VM's network interface working hard, suggesting that it's downloading that big APOD picture. In contrast, if I run my solution above in that same environment (with caches cleared, etc., etc.), I don't see the VM network interface doing that work, suggesting that the download is either not being started or is being aborted early on.
.text *:not(img) will select every descendant from .text that is not an image, so in theory it should work.