Webaudio timing performance - web-audio-api

The file below uses ToneJS to play a steam of steady 8th notes. According to the log of the timing, those 8th notes are precisely 0.25 seconds apart.
However, they don't sound even. The time intervals between the notes are distinctly irregular.
Why is it so? Is there anything that can be done about it? Or is this a performance limitation of Javascript/webaudio-api? I have tested it in Chrome, Firefox, and Safari, all to the same result.
Thanks for any information or suggestions about this!
<!DOCTYPE html>
<html>
<head>
<title>Tone Timing Tester</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.32/Tone.min.js"></script>
</head>
<body>
<button onclick="start()">Start</button>
<button onclick="stop()">Stop</button>
<form>
<input id="bpm" type="number" value="120">
<button type="button" onclick="submitBPM()">Enter BPM</button>
</form>
<script type="text/javascript">
var synth = new Tone.Synth().toDestination()
Tone.Transport.scheduleRepeat(function(time){
console.log('time', time);
synth.triggerAttackRelease('C4', '8n')
}, "8n");
async function start() {
await Tone.start()
Tone.Transport.start();
}
function stop() {
Tone.Transport.stop();
}
function submitBPM() {
var bpm = document.getElementById('bpm').value;
Tone.Transport.bpm.value = bpm;
}
</script>
</body>
</html>

For a scheduled triggerAttackRelease, you should pass the time value as the third argument.
Tone.Transport.scheduleRepeat(function(time){
console.log('time', time);
synth.triggerAttackRelease('C4', '8n', time);
}, "8n");
Here's a codepen that contains the working code.

Related

After login into my website protractor unable to find next element

This code for after login user should post some hello message.
describe("launching Telekha",function(){
it("navigating to signin page",function(){
browser.get("www");
element(by.model("credentials.email")).sendKeys("abnsd6#gmail.com");
element(by.model("credentials.password")).sendKeys("123456");
var ptr = element( by.css('[ng-click="login()"]') );
ptr.click();
});
it("on dashboard",function(){
element(by.model("post.postText")).sendKeys("hello");
element( by.css('[ng-click="postit()"]') ).click();
});
});
HTML code for the button
<textarea id="post-editor" placeholder="Tell your friends" ng-model="post.postText" class="textareanoborder col-xs-12 col-md-12 ng-pristine ng-valid ng-isolate-scope ng-touched" autocomplete="off" aria-invalid="false"> </textarea>
This is a guess, but from experience it usually isn't far off. When the page you are navigating to is loaded, are there any animations, AJAX requests or other delays, separate from AngularJS that might be occurring? Protractor does not wait for these and will execute the code:
element(by.model("post.postText")).sendKeys("hello");
as soon as it can. If when this is executed, all the animations have not yet been completed, you element will not be visible to protractor and you'll see the error you've encountered.
To quickly test this, add a browser.sleep(10000); before the line where it's failing. browser.sleep(); is a command that will force the browser to wait for a set amount of time, in this case 10.000 milliseconds (10 seconds).
If this does end up working, you might want to change it to something more elegant such as:
browser.wait(function() {
return element(by.model("post.postText")).isPresent();
}, 10000);
Which will wait for your element to become visible, but only for a maximum of 10 seconds, after which it will continue anyway.
EDIT1: (which works fine):
HTML (Serving from http://localhost:8080)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Example</title>
<script src="bower_components/angular/angular.min.js"></script>
<script>
var app = angular.module('app', []);
app.controller('myCtrl', function($scope) {
});
</script>
</head>
<body ng-app="app" ng-controller="myCtrl">
<textarea id="post-editor" placeholder="Tell your friends" ng-model="post.postText" class="textareanoborder col-xs-12 col-md-12 ng-pristine ng-valid ng-isolate-scope ng-touched" autocomplete="off" aria-invalid="false"> </textarea>
</body>
</html>
spec.js
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
browser.get('http://localhost:8080');
$("#post-editor").sendKeys("testlol");
browser.sleep(2000);
});
});
conf.js
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['spec.js']
};
You can implement protractor.ExpectedConditions.visibilityOf() method to wait until element is visible on UI and then send data to input field.
var EC=protractor.ExpectedConditions;
it("on dashboard",function(){
var ele=element(by.id("post-editor"));
browser.wait(EC.visibilityOf(ele),8000,'Ele is not presented');
ele.sendKeys("hello");
element( by.css('[ng-click="postit()"]') ).click();
});

Google Map interfering with button event in Safari

If you press "Upload your file" in the test code below, the button text should change to "Uploading...", although in Safari it doesn't. Selecting a sufficiently large file should just give you time to observe the button. Comment out map div and it all works fine.
Here's the isolated test code, it took half a day to find that the map could be the cause, but I need to dig deeper. Interestingly, when copying the same code to JSfiddle it will not reproduce the bug, so you'll need to create your own html file.
<!DOCTYPE html>
<html>
<head>
<title>Google Map interfering with button event in Safari</title>
<meta charset="utf-8">
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script>
var map;
function init() {
var options = {
zoom: 8,
center: new google.maps.LatLng(0, 0),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map'), options);
}
google.maps.event.addDomListener(window, 'load', init);
</script>
</head>
<body>
<form action="http://localhost" method="POST" enctype="multipart/form-data">
<!--comment map to see correct button behaviour in Safari-->
<div id="map" style="width:300px;height:300px"></div>
<p><small>Very large file please:</small> <input type="file" name="big"></p>
<button onclick="this.innerHTML='Uploading...'">Upload your file</button>
</form>
</body>
</html>
There were similar questions on SO before, but no solutions.

Embedded YouTube in iOS Safari just spins

I have a number of very small pages which basically loads a video for use on mobile browsers. In the latest iOS (5.1.1) the video player loads, but then just spins. The video can be played by clicking on the video, but since it looks like it's still loading, people may not do that. Works everywhere else.
I'm using the youtube api code
This is a test version of the page.
http://bit.ly/S2KuN1
Here's the code:
<!DOCTYPE HTML>
<html>
<head>
<title>Celebrity's Caribbean Shore Excursions</title>
<link href="http://www.celebritycruises.com/css/min/global.min.css" rel="stylesheet">
<link href="http://media.celebritycruises.com/celebrity/content/en_US/css/m_video.css" rel="stylesheet">
<script language="JavaScript" type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript" src="http://www.youtube.com/player_api"></script>
<script type="text/javascript">
// create youtube player
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
videoId: 'PH-m591p4xg',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
</script>
<script type="text/javascript" src="m_video.js"></script>
</head>
<body id="video_page">
<div class="logo"><img src="http://media.celebritycruises.com/celebrity/content/en_US/images/cel_misc/logo.jpg" alt="Celebrity Mobile" width="259" height="55" border="0"></div>
<div id="player"></div>
<div class="visit">
<p>Now visit Celebrity Cruises' official mobile website.</p>
<p><a class="ccButton large" target="_self" href="http://m.celebritycruises.com/m/home.do"> <span class="text">Explore</span> <span class="pointer"> </span> </a></p>
</div>
</body>
</html>
And here's the js for the page
// JavaScript Document
// autoplay video
function onPlayerReady(event) {
event.target.pauseVideo();
}
// when video ends
function onPlayerStateChange(event) {
if(event.data === 0) {
window.location = "http://m.celebritycruises.com/m/home.do";
}
}
//style Blackberry
window.onload = function() {
var ua = navigator.userAgent;
if (ua.indexOf("BlackBerry") != -1 ) {
document.getElementById("video_page").className = "bb";
}
};
Okay, I figured out how to solve it.
I changed
event.target.pauseVideo();
to
event.target.stopVideo();
I don't know what the issue is, but iOS 5.1.1 never seems to finish autoloading. Using the stopVideo() command from the YouTube api stops the process and the play button appears. Once clicked, it loads normally.

Jquery mobile form response page does not download content from pageinit until refresh of page

I am new to jquery mobile, and am having problems getting content I have inserted dymically using pageinit to display on the first time of the form response page. It displays on subsequent refreshes of the page. I also don't want the content to cache.
I need to use querystring values like ?blah=1&blah=2 as I use these in my call to an external json file.
How should I be doing this? If I use rel="external", and setting ajax to false, I have problems with issues on android. So using pageinit in the header, how do I make the dynamically loaded content (in the example, the time in seconds) in the 2nd page display first time round?
I have simplified the problem into test pages below.
Expected behaviour. When you click on the submit button of the form you go through to the 2nd page which should display the no of seconds taken from datetime
Actual behaviour. The seconds/time does not display on the 2nd page until the page is refreshed.
Elsewhere, I have come across the suggestion to put the pageinit code into the div itself, however this has caused the content to cache on android (ie the no of seconds remains the same), so I don't want to do this.
Any ideas on how I should approach this would be much appreciated
Sample code
=======
Page 1 - form
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
<script src="/scripts/myinit.js"></script>
</head>
<body>
<div data-role="page" id="page1" data-add-back-btn="true">
<div data-role="content" data-theme="b">
<form action="page_2.htm" method="GET" id="form1" name="form1">
<input type="hidden" name="seconds" value="">
<div class="ui-block-b"><button type="submit" data-theme="a">Submit</button></div>
</form>
</div>
</div>
</body>
</html>
===
Page 2 form response page
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script src="http://code.jquery.com/mobile/1.0.1/jquery.mobile-1.0.1.min.js"></script>
<script src="/scripts/myinit.js"></script>
</head>
<body>
<div data-role="page" id="page2" data-add-back-btn="true">
<div id="job" data-role="content">
</div>
</div>
</body>
</html>
===
custom javascript file called /scripts/myinit.js (included in both pages above)
$('#page1').live('pageinit', function(event) {
var seconds = new Date().getTime();
$('input[name=seconds]').val(seconds);
});
$('#page2').live('pageinit', function(event) {
var querystring = location.search.replace( '?', '' ).split( '&' );
var queryObj = {};
for ( var i=0; i<querystring.length; i++ ) {
var name = querystring[i].split('=')[0];
var value = querystring[i].split('=')[1];
queryObj[name] = value;
}
var seconds = queryObj["seconds"];
$('#job').append("seconds=" + seconds);
});
try changing pageinit by pageshow. i had the same problem and it worked for me
Link to the external file like this:
HTML --
I'm a Link
JS --
$(document).delegate('#external-link', 'click', function () {
$.mobile.changePage('/path/to/file.html', { reloadPage : true });
return false;
});
Setting the reloadPage option for the changePage() function will allow the external page to be refreshed rather than loading the cached version. Since the external page will be refreshed, the pageinit code for it will run when it's initialized and your code should function properly.
Documentation: http://jquerymobile.com/demos/1.1.0-rc.1/docs/api/methods.html

Changing jQuery Mobile date picker options

I am using the jQuery Mobile date picker control but I cannot change any of the properties. For example, the following code will attempt to set the first day of the week to Wednesday, in the document ready function but it doesn't work.
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<link rel="stylesheet" href="jquery.mobile-1.0a4.1.min.css" />
<link rel="stylesheet" href="jquery.ui.datepicker.mobile.css" />
<script type="text/javascript" src="jquery-1.5.2.min.js"></script>
<script type="text/javascript" src="jquery.mobile-1.0a4.1.min.js"></script>
<script src="jquery.ui.datepicker.js"></script>
<script src="jquery.ui.datepicker.mobile.js"></script>
<script>
$(document).ready(function()
{
$("#date").datepicker({ firstDay: 3 });
});
</script>
</head>
<body>
<div data-role="page" data-theme="b" id="home">
<div data-role="header">
<h1>Test</h1>
</div>
<div data-role="content" data-theme="b">
<div data-role="fieldcontain">
<input type="date" name="date" id="date" value="" />
</div>
</div>
</div>
</body>
</html>
Any ideas what I'm doing wrong? Is this failing to work because the date picker is already displayed?
I have managed to change to change the properties by changing the "jquery.ui.datepicker.mobile.js" file
To change the date format to : "dd/mm/yy" Code shown below
//bind to pagecreate to automatically enhance date inputs
$( ".ui-page" ).live( "pagecreate", function(){
$( "input[type='date'], input[data-type='date']" ).each(function(){
$(this).after($("<div />").datepicker({ altField: "#" + $(this).attr("id"), showOtherMonths: true, dateFormat: "dd/mm/yy" }));
});
});
What happens if you try a different setter?
$('.selector').datepicker('option', 'firstDay', 1);
Your missing the bind()
//reset type=date inputs to text
$( document ).bind( "mobileinit", function(){
$.mobile.page.prototype.options.degradeInputs.date = true;
});
Docs (At the bottom of the page): http://jquerymobile.com/demos/1.0a4.1/experiments/ui-datepicker/
Also I like the DateBox a littler better IMO: http://dev.jtsage.com/jQM-DateBox/
I had a similar problem and had to change two lines in jquery.ui.datepicker.mobile.js
First, I needed to store the return value of the call to prevDp (line 18).
var value = prevDp.call( this, options );
Then I had to return this value instead of this (line 46)
return value;
Now you should be able to call the datepicker with options like Matt Ball suggested.