How to Target Milliseconds in the Timeline?


(Matt) #1

I’m trying to change elements every 833 milliseconds for the purposes of syncing up with music. The music is at 72 BPM’s. The quarter note lag is therefore 833 milliseconds which is the frequency I need the element to change.

Can anyone suggest the best way to do this?

Thank you!
Matt


(Hans-Gerd Claßen) #2

a sync should be initialized by an event. using a html5 audio or videoelement you could listen to timeupdate.

but if you really would like to change an element in a certain interval you could use

setInterval(function(){/*do something*/}, 833 /*intervaltime in milliseconds*/)


(Matt) #3

Thank you @h_classen

I’m using the API for an audio web app that is embedded so I don’t have an audio or video element.

I will have many different events occur every 833 milliseconds once a timeline begins . Can I use your example in conjunction with a timeline somehow?
Here is my example:
noteflightTest_MHv1.hype.zip (28.0 KB)


(Mark Hunte) #4

Looking at the API of the noteflight, it does have some events that you can tap into. Unfortunately I cannot see an obvious timestamp event or note played…

There are some you could use like the playbackRequest and playbackStop to start the animation and stop the animation by adding the below to the init function noteflight() and removing the action to start the playMelody function from the play button.

window.scoreView.addEventListener('any', function(event) {
   
    for (var prop in event) {
     
      
      switch(event[prop]) {
    case 'playbackRequest':
       hypeDocument.startTimelineNamed('playMelody', hypeDocument.kDirectionForward)
        break;
     case 'playbackStop':
       hypeDocument.pauseTimelineNamed('playMelody')
        break;    
        default:
       return;
}
    }
  });

(Matt) #5

Hi @MarkHunte - I have a quick question about the code you provided above for the eventListener.

I understand basically how it works but I’m unsure how I would pass in other parameters that Noteflight provides.

For example I’d like to use the event listener type =“selectionChange” with some of these parameters:

So how might I add the startIndex and endIndex with this:

  	window.scoreView.addEventListener('any', function(event) {

for (var prop in event) {
 
  
  switch(event[prop]) {
case 'selectionChange':
   hypeDocument.startTimelineNamed('playMelody', hypeDocument.kDirectionForward)
    break;
 case 'playbackStop':
   hypeDocument.pauseTimelineNamed('playMelody')
    break;    
    default:
   return;

}
}
});

Thank you!
Matt


(Mark Hunte) #6

Hi @matt5834,

Remember we are listening for an event. In the case we are listening for any event note flight fires.
(You are not passing in anything)

Hence the ‘any’ in the start of the addEventListner function.

The switch statement is then used to catch any particular events by the Type.

We could have done event.type instead of event[prop] to make it clearer.

Any way what we actually have is an Event and we look at the Events Type in the switch, if it matches one of our cases we do stuff.

But the important thing to realise is that we can get any of the other properties from the Event just as we have it’s Type.

So once we have an Event and we know it is Type selectionChange, we can get any of it’s properties by using the same dot syntax method on the end of the Event.

i.e

event.startIndex

event.endIndex

I assume you would want to make them into global vars so you can use them elsewhere.

i.e

window.noteflightStartIndex = event.startIndex;

Below are the properties I get when I log the event to console.

[Log] Object

embedId: "score1"

endIndex: 1

endOffset: 3

fragment: "#o0/1/1/2/0"

kind: "object"

staffIndices: [0] (1)

startIndex: 1

startOffset: 2

target: ScoreView {getSubstringIndex: function, iterateObject: function, handleMessage: function, dispatchMessage: function, applyMethod: function, …}

timestamp: 1488237256252

type: "selectionChange"

(Matt) #7

Thanks so much for the very clear description @MarkHunte !

Before seeing if I am writing the code wrong I have a question: Because the event listener is listening to anything that Noteflight is firing am I correct in thinking that if one of the event types is selectionChange with one parameter being “fragment” anytime that a selection in a certain specified fragment changes it will fire and then we can tell Hype how to handle it.

For example, I know that a certain fragment is called #m2 (this is all of the notes in the second measure). I want to have Noteflight only fire something to the eventListener when a selection in measure 2 is changed.
I’m writing it like this which I’m certain is incorrect:

window.scoreView.addEventListener('any', function(event) {

for (var prop in event) {
 
  
  switch(event[prop]) {
case 'selectionChange.event.fragment("#m2")':
   hypeDocument.startTimelineNamed('melody', hypeDocument.kDirectionForward)
    break;
 case 'playbackStop':
   hypeDocument.pauseTimelineNamed('melody')
    break;    
    default:
   return;

}
}
});


(Mark Hunte) #8

The eventListener is listening for all events from Notefilght.
We could just listen to specific event types by replacing the any with a type name

Have a look at

To get a better understanding of switches and what I am explaining below.

In the switch we are using the type name as the expression switch(event[prop]) to match with a case

So each case needs to represent one of the event types as a string name.

When the expression matches a case, in this case selectionChange the code block after the case is run.

It is in the code block that you need to then evaluate if the fragment matches what you want and then act accordingly.

I have already shown how to get that from the event. So use that in an if statement block in the switch code block.


(Matt) #9

Thanks for your help Mark. After spending all day yesterday trying to understand switches I realized that I need to get more comfortable using eventListeners in general.

I’m trying to have anytime something in the 2nd measure of my Noteflight score (parameter = #m2) is changed it triggers a timeline (melody).

I’m using this code

 function startTime(evt) {
 window.scoreView.selectFragment("#m2");
 hypeDocument.startTimelineNamed('melody', hypeDocument.kDirectionForward);

 }

window.scoreView.addEventListener('selectionChange', startTime);

The result is that whenever I simply click on the Noteflight score the timeline is triggered.

I’m sure I’m missing something basic.
Noteflight Interactive2.hype.zip (22.2 KB)


(Mark Hunte) #10

Will look at this later today, but the code is not right.

You want something like this

			 NFClient.init(function(info) {
   // alert("Noteflight API is ready, version " + info.version);
   
   var options = {
   host: 'yciw.learning.noteflight.com',
width: 680 ,
height: 320,


 
 
viewParams: {
  scale: 3,
 app: 'html5',
 hidePlaybackControls: true,
 hideFullWindow: true,
 role: 'template',
 displayMode: 'strip'
 
 
 
 

}
  };
  window.scoreView = new NFClient.ScoreView('score2', '5b51707b72828726b52568e84153ddea1c61fd59', options);
  
 
 
  function startTime(evt) {
 
 console.log(evt.fragment );
   if (evt.fragment == "#m2"){
   
hypeDocument.startTimelineNamed('melody', hypeDocument.kDirectionForward);
   }
 }

window.scoreView.addEventListener('selectionChange', startTime);

  });
 

hypeDocument.getElementProperty(score2, 'left');
hypeDocument.setElementProperty(score2, 'left', 180);

hypeDocument.getElementProperty(score2, 'top');
hypeDocument.setElementProperty(score2, 'top', 190);

(Matt) #11

Thanks Mark. I’m stumbling around in the dark a bit but I’ve gotten this to work:

  window.scoreView.addEventListener('selectionChange', (event) => {
  
     if (event.fragment == "#o0/1/1/3/0") {
     
     hypeDocument.startTimelineNamed('melody', hypeDocument.kDirectionForward);
     
     }
  
  
 });

(Mark Hunte) #12

Yep. Thats the individual note fragment. The #m2 will be clicking at the top of the score.