More MIDI data



  • To run any sort of sequencer or tempo linked modulations etc. we are going to need MIDI Song Position Pointer data, we will need to know where we are in the song and the bar as well as Transport Start and Transport stop. I cant see this info anywhere in the current function calls.



  • This data is accessible through the common plugin-wrapper from JUCE (copied from their API):

    @3a2jqf1l:

    double bpm
    The tempo in BPM. More…

    int timeSigNumerator
    Time signature numerator, e.g. More...

    int timeSigDenominator
    Time signature denominator, e.g. More...

    int64 timeInSamples
    The current play position, in samples from the start of the edit. More...

    double timeInSeconds
    The current play position, in seconds from the start of the edit. More...

    double editOriginTime
    For timecode, the position of the start of the edit, in seconds from 00:00:00:00. More...

    double ppqPosition
    The current play position, in pulses-per-quarter-note. More...

    double ppqPositionOfLastBarStart
    The position of the start of the last bar, in pulses-per-quarter-note. More...

    FrameRateType frameRate
    The video frame rate, if applicable. More...

    bool isPlaying
    True if the transport is currently playing. More...

    bool isRecording
    True if the transport is currently recording. More...

    double ppqLoopStart
    The current cycle start position in pulses-per-quarter-note. More...

    double ppqLoopEnd
    The current cycle end position in pulses-per-quarter-note. More...

    bool isLooping
    True if the transport is currently looping. More...

    I could wrap them up into an Object which could be retrieved by this scripting call:

    Engine.getPlayHead().ppqLoopStart; // contains the current value for this property
    
    

    Although this will be a thing that is heavily dependant on the host DAW and needs some broad testing. I can check Cubase (5 + 8), Reaper 5 and Ableton 9 on both OSX and Windows.



  • Well I dont think we need all of these, I think we need the following:

    bool isPlaying

    … but more importantly we need a call back that happens when this value is changed, in KSP its on Listener

    bool ppqPosition

    assuming this is 96ppq resolution, which is a bit too fine I imagine, it will add a lot of overhead into the engine, KSP allows me to set the resolution here from 1 to 24 , and again when this happens we need a call back

    So in some on-listener-type call back we need to interrogate some variable to see what type it is:

    SIGNAL_TYPE would need to be

    TRANSPORT_START
    TRANSPORT_STOP
    POSITION

    the following would be useful to get from a javascript call as you suggest:

    double bpm
    int timeSigNumerator
    int timeSigDenominator
    double ppqPositionOfLastBarStart
    double ppqLoopStart
    double ppqLoopEnd
    bool isLooping

    As you say not all DAWs in all situations will return these, but my experience is that all DAWs on all platforms return the vital transport and position data.



  • Well, I added all properties (there's almost no overhead and some other properties may get interesting for other use cases).

    Alright, I added two callbacks (although you have to write them manually, I don't want to clutter the editor interface with too many callback buttons):

    // Callback that is called periodically and synchronized to the host (only called if the host is playing)
    function onClock(timeStamp);
    
    // Callback that indicates the transport change (isStart is 1 if the host is playing)
    function onTransport(isStart);
    
    

    Getting the timing right was rather complicated and the timing goes funky when the audio buffer size is bigger than the clock interval - but this should not be relevant in the real world.

    Every sound generator has its own clock and can be set from one bar (=4 quarters) to 32th notes (faster doesn't make sense). You can change the clock speed with the API call

    Synth.setClockSpeed(16); // would be 16th notes..
    Synth.setClockSpeed(0); // stops the clock (this is also the default).
    
    

    You can use the host info API call to write a sequencer like this (make sure you use this on a sound generator with fast decay and no sustain!):

    // A fast C-minor arpeggiator synched to the host
    
    Synth.setClockSpeed(32);
    
    var x = [48,51,55,60,63,67,72,75];
    var index = 0;
    
    function onClock()
    {
    	// You need to round up the ppqPosition because it is the value at the start of the buffer
    	index = parseInt(Math.ceil(Engine.getPlayHead().ppqPosition*8.0));
    
    	Synth.playNote(x[index%x.length],127);
    }
    
    

    This should be everything you need to write a sequencer. If you need more, let me know.



  • yeah this should work, where would I write the onClock and onTransport functions, anywhere special?

    … you wont believe how long I've been asking UVI for this functionality in their engine... nice turn around, well done.



  • Thanks. I wanted to add this feature for a long time because the onTimer callback is not perfectly accurate (it gets executed at the start of the buffer, so you don't have sample accurate timing).

    You can write them anywhere (outside of the other callbacks of course), but I would suggest adding them in the onInit page - the script processor checks if the other callback pages are empty and skips the callback to save overhead and if you add the onClock callback on the onNoteOn page, an empty callback will be executed for every incoming note on event.


 

4
Online

353
Users

1.1k
Topics

7.6k
Posts