Arrays / loops with midi.js - arrays

I am looking to use a playsound function in midi.js to loop an array, with chords that i select, maybe 4 diff ones. But i can't figure it out. I can get it to do a single array, but not multiple, and it does not loop, only plays the amount of time I set it to (now 8).
window.onload = function () {
MIDI.loadPlugin({
soundfontUrl: "../MIDI.js/examples/soundfont/",
instrument: "acoustic_grand_piano",
onprogress: function(state, progress) {
console.log(state, progress);
},
onsuccess: function () {
for (var i = 0; i < 9; i++){
playsound([37,59,61,71,80])}
}});
var delay =1;
function playsound($chords)
{
var velocity = 127;
MIDI.setVolume(0, 127);
MIDI.chordOn(0, $chords, velocity, delay);
MIDI.chordOff(0, $chords, delay+1);
delay += 1;
}

Your code should work, except that for the timing to work predictably, I found you have to wait a bit after the success callback is called. If you call right after load, notes are played irregularly and out of sequence.
I recommend using a function like playChords below and testing well after load by calling the function with a button press. For example, this function plays three different chords at 1/2 second intervals, a total of 9 times.
chords = [[37,59,61,71,80],[38,60,62,72,81],[39,61,63,73,82]];
function playChords() {
for (var i = 0; i < 9; i++){
playChord(i/2, chords[i%chords.length]);
}
}
function playChord(delay, chord) {
MIDI.chordOn(0, chord, 127, delay);
MIDI.chordOff(0, chord, delay+1);
}

Related

How to update certain element of array in TypeScript

I have an array looking like this
[0...99]
0: {field1: 'test1', field2: test2}
1: {field1: 'test3', field2: test4}
...
I created the array like this
array:any = [];
And I added data using push() how can i update the data of element n later?
Initialisation part
for(this.j = 0; this.j< this.accNum; this.j++) {
this.array.push({ field1:'test1', field2:'' });
this.http1
.get(this.url.concat(this.array[this.j]?.field1))
.subscribe((response1: any) => {
this.array[this.j].field2 = 'test2';
});
}
Ok, seeing your code in the comment, I think I now know what it's happening.
The problem is that you are using a variable whose value has long been updated. Take a look at the following code:
let i;
for (i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
Notice the following:
The i variable is declared outside of the loop (this mimics your this.j behaviour).
The loop finishes completely before the first console.log has a chance to run (basically, the setTimeout make the code to run asynchronously). In this case, the setTimeout mimics your API call.
By the time the setTimeout runs, the loop has completely run leaving the i variable with the value of 5. So, sure enough, if you run this code you will see that you get 5 printed 5 times instead of the expected 0, 1, 2, 3, and 4.
Also notice that the printed value is 5 instead of the last "correct" value for this loop which would be 4. If you translate this to arrays, the result is that you are always accessing the element length + 1 which, obviously it does not exist.
What you need to do to fix your problem is to scope the the value of this.j inside the loop. For example:
let i;
for (i = 0; i < 5; i++) {
((k) => {
setTimeout(() => {
console.log(k);
}, 1000);
})(i);
}
With this code, what I'm doing it to create an anonymous function inside the loop and passing i to it (which gets it's value as k). By doing this, I'm setting k in the scope with the right value for the setTimeout and console.log to run.
Now, if you run this code, you will see that the result is, indeed, the expected one.
A more elegant way of doing this is by calling an external function instead:
function requestAPI(i) {
setTimeout(() => console.log(i), 1000);
}
let i;
for (i = 0; i < 5; i++) {
requestAPI(i);
}
Here it is a more suitable example for your case:
const values = [];
function requestAPI(i) {
setTimeout(() => {
values[i].field2 = 'test2';
}, 1000);
}
let i;
for (i = 0; i < 5; i++) {
values.push({ field1: 'test1', field2:'' });
requestAPI(i);
}
setTimeout(() => console.log(values), 2000);
You can run it here.
I suggest you search for the topic of variable scope in Javascript.

Angular $timeout calls function only once

I am using AngulaJs and Ionic.
I am trying to call a function every 3 seconds for 10 times only (this is why I am not using $interval). Thing is, the code below calls the function only once (while the debugging code to the console is called 10 times).
for (i = 0; i < 10; i++)
{
$timeout(function () {
$scope.play(); // this is the called function
console.log("progress bar: " + i);
}, 3000);
}
Any help will be appreciated,
Thanks in advance,
Pagugim
For loop will start 10 timeouts all together. But seems like you want them to execute one after another. For this case you can use recursion.
var count = 0;
replay();
function replay() {
$timeout(function () {
if(count < 10) {
$scope.play();
count++;
replay(); // call this function again
console.log("progress bar: " + $scope.progressVal);
}
}, 3000);
}
I am trying to call a function every 3 seconds for 10 times only
You can use the optional count argument of $interval.
var i=0;
function reportProgress() {
$scope.play(); // this is the called function
console.log("progress bar: " + i);
i++;
};
$interval(reportProgress, 3000, 10);
From the Docs:
$interval
Usage
$interval(fn, delay, [count], [invokeApply], [Pass]);
count (optional) Number of times to repeat. If not set, or 0, will repeat
indefinitely. (default: 0)
-- AngularJS $interval API Reference
Actually, the $scope.play() is called 10 times, but at almost the same time. To call the function every 3 seconds, you can use a closure to keep the value i and set timeout as 3000*i. I think this is what you want:
for (i = 0; i < 10; i++) {
(function(i) {
$timeout(function() {
$scope.play();
console.log("progress bar: " + i)
}, 3000 * i);
})(i);
}
Jsfiddle: https://jsfiddle.net/ealonwang/ens9wu0g/15/. Watch the value and console log change every 3 seconds.

Cycle array of functions with TimerEvent AS3

i'm working on a movieclip that features a small array of functions that i'd like to execute at certain intervals.
Here is the code:
var Tmr:Timer = new Timer(1000, 3);
var funcArray:Array = [move1, move2, move3];
var i:Number = 0;
funcArray[i]();
function kick(){
if (i < 2) {i++; funcArray[i](); trace("GO NEXT");}
else {i = 0; funcArray[i](); trace("FROM-START");}
}
function timerX():void{
Tmr.start();
Tmr.addEventListener(TimerEvent.TIMER_COMPLETE, outro);
trace("TIMERSTART");
}
function outro(e:TimerEvent):void{
e.currentTarget.removeEventListener(TimerEvent.TIMER_COMPLETE, outro);
trace("TIMEREND");
kick();
}
function move1(){
item1.x = 100;
item1.y = 100;
trace("MOVE1");
timerX()
}
function move2(){
item1.x = 300;
item1.y = 400
trace("MOVE2");
timerX();
}
function move3(){
item1.x = 800;
item1.y = 600
trace("MOVE3");
timerX();
}
As you see i've filled it with trace commands because... The dark magic happens after move1() has been played: apparently it starts looping the functions EVERY SECOND instead of 5, like it is depending on an Enter_Frame event.
The trace sequence in output is: MOVE1, TIMERSTART, TIMEREND, MOVE2, TIMERSTART, GO NEXT, TIMEREND, MOVE3, TIMERSTART, GO NEXT, TIMEREND, MOVE1, TIMERSTART, FROM-START...
instead of: MOVE1, TIMERSTART, TIMEREND, GO NEXT, MOVE2, TIMERSTART, TIMEREND, GO NEXT etc.
I also tried to move the kick() function up in the outro one
function outro(e:TimerEvent):void{
kick();
e.currentTarget.removeEventListener(TimerEvent.TIMER_COMPLETE, outro);
trace("TIMEREND");
}
And the effect is that it doesn't go past move2().
Where do you think is the error? What was wrong in the logic i used to write down this?
Thanks for your kind help!
Per the timer firing every second, you've defined the Timer to dispatch timer events every 1-second; although, repeating 3 times before firing TIMER_COMPLETE.
var Tmr:Timer = new Timer(1000, 3);
If you want the timer to fire every 5-seconds, you could just:
var Tmr:Timer = new Timer(5000);
Next up, you never stop or reset the timer. You can stop() and start() the timer; or, if you're depending on a specific number of times for the timer to repeat, then reset() the timer.
Since it seems like you simply want an event to fire every 5-seconds infinitely, there's really no need to start / stop / reset / add new listeners - just start the timer and listen for timer events:
var timer:Timer = new Timer(5000);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
function timerHandler(event:TimerEvent):void {
// timer fired
}
Putting this all together, if you just want to go from: move1, move2, move3 infinitely repeating:
import flash.utils.Timer;
import flash.events.TimerEvent;
var timer:Timer = new Timer(5000);
timer.addEventListener(TimerEvent.TIMER, timerHandler);
timer.start();
var actions:Array = [move1, move2, move3];
var i:uint = 0;
function timerHandler(event:TimerEvent):void {
actions[i]();
if(++i >= actions.length)
i = 0;
}
function move1():void {
trace("move1");
}
function move2():void {
trace("move2");
}
function move3():void {
trace("move3");
}
function exit():void {
timer.reset();
timer.removeEventListener(TimerEvent.TIMER, timerHandler);
timer = null;
}

how to add parameter to the function in angular interval

I'm writing a function which will iterate through an array with some time interval.
eg:arr=[1,2,3,4,5,6,7]
And I want to show each element with angular interval.
$interval(step,5000,7);
function step(i){
console.log(arr[i]);
}
So it'll output the numbers inside the array in a 5 seconds' interval. How can I pass the parameter into the function? Thanks.
You could also do this way:
var arrOrig =[1,2,3,4,5,6,7],
arr = angular.copy(arrOrig), //if you want it for later
cancelPromise = $interval(step, 5000);
function step() {
console.log(arr.shift()); //take out the item from top of array
if (!arr.length) { //once array runs out
$interval.cancel(cancelPromise); //cancel interval
}
}
var arr = [1, 2, 3, 4, 5, 6, 7];
var $interval = angular.injector(['ng']).get('$interval');
var cancelPromise = $interval(step, 5000);
function step() {
console.log(arr.shift());
if (!arr.length) {
$interval.cancel(cancelPromise);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
arr.forEach(function (number, idx) {
$timeout(function () {
console.log(number);
}, 5000 * (idx + 1));
});
You want to use $timeout rather than $interval because calling $interval will set up an interval each time for each number and it won't stop until it's cleared explicitly.
var i=0;
$interval(function(){
step(i);
i++
},5000);
function step(i){
console.log(arr[i]);
}
This code will display the numbers one at a time, on repeat:
var getNextNumber = (function(){
var i = 0;
var numbers = [1,2,3,4,5,6,7];
return function(){
if(i >= numbers.length){
i = 0;
}
return numbers[i++];
};
})();
$interval(function(){
var number = getNextNumber();
console.log(number);
},5000);
See this working jsfiddle.
If you want to display them only once, use one of the $timeout solutions from one of the other answers. $interval is all about repeating some command for an indefinite amount of time, whereas $timeout is more about performing a command once in the future. See this answer for more.

Angularjs freezes in loop suing $timeout to call function multiple times sequential after or less than a second?

I want to be able to call a function repeatedly every second (or faster).
Assuming "startdate" contains the starting date in the text... In my controller:
scope.arrayoftimes=[{time:XXXX},{time:XXXXXX}]
scope.startdate = $('#startingdate'); //start date comes from text in an element
scope.speed = 1000;
scope.index = 0;
scope.timer = $timeout(scope.doSomething(), scope.speed);
scope.doSomething = function() {
var currentTime = scope.startDate.html();
for (var i=scope.index; i < scope.arrayoftimes.length; i++) {
if(currentTime == roundTo(scope.arrayoftimes[i].time)) {
$rootScope.$emit('timeMatch', scope.index);
scope.index++;
break;
}
}
$timeout(scope.doSomething(), scope.speed);
}
The above code frezes my browser on execution. HOWEVER if I stick in an "alert" anywhere after the scope.index++ or inside the if condition, then it works fine (as long as I check "Prevent opening multiple dialog boxes")
scope.arrayoftimes=[{time:XXXX},{time:XXXXXX}]
scope.startdate = "//Insert Starting Date Here"
scope.speed = 1000;
scope.index = 0;
scope.timer = $timeout(scope.doSomething(), scope.speed);
scope.doSomething = function() {
var currentTime = scope.startDate;
for (var i=scope.index; i < scope.arrayoftimes.length; i++) {
if(currentTime == roundTo(scope.arrayoftimes[i].time)) {
$rootScope.$emit('timeMatch', scope.index);
scope.index++;
alert("alert"); // the existence of this alert message and checking prevent from opening more dialog boxes in chrome allows this to execute fine without freezing
break;
}
}
$timeout(scope.doSomething(), scope.speed);
}
What is going on? How can I get around this? Is there a better way to do this?
Your timeout call probably needs to be:
$timeout(function() { scope.doSomething(); }, scope.speed);
or
$timeout(scope.doSomething, scope.speed);
Otherwise you're executing doSomething() immediately which causes an infinite loop.

Resources