Logic Pro X Midi FX Javascript - 使用GetTimingInfo()会导致100%的CPU

时间:2014-07-20 23:19:16

标签: javascript midi

我在MainStage 3中使用Scripter MidiFX(与LogicPro X相同)使用javascript创建自定义琶音器,因为我需要更多控制,因此使用GetTimingInfo()获取当前节拍是合乎逻辑的正如我在他们的例子中看到的那样,ProcessMIDI()函数触发MIDI音符。不幸的是,这甚至在我的Core i7 MacBook Pro上都占用了CPU,我在实际演出时使用的是带有Core 2 Duo处理器的MacMini。

我能够编写自己的代码来使用新的Date()来计算当前的节拍,然后只使用GetTimingInfo()来获取当按下新音符时的当前节拍,但即使这样也不能保持我喜欢的CPU。

当我不包括" NeedsTimingInfo = true"并且只是硬编码速度一切都很好,但这是很多额外的代码,使用内置函数更有意义。

这是一个简单的音序器示例导致此问题...我做错了什么?即使我使用计数器仅在每第16次调用时运行ProcessMIDI(),也会发生这种情况!

NeedsTimingInfo = true;

var startBeat = -1;
var startPitch = 0;
var lastBeat = -1;
var currentStep = 0;

// melody test
var steps = [
    0, 0, 0, 0, 0, 0, 0, 2,
    0, 0, 0, 0, 0, 0, 2, 5
];

function HandleMIDI(e) {
    if (e instanceof NoteOn) {
        if (startBeat > 0) return;

        currentStep = 0;
        startPitch = e.pitch;
        var info = GetTimingInfo();
        lastBeat = startBeat = info.blockStartBeat; 
        doNote(e.pitch);
    }
    else if (e instanceof NoteOff) {
        if (e.pitch == startPitch) {
            startBeat = -1;
        }
    }
    else {
        e.send();
    }
}

function doNote(pitch) {
    var adjustment = 0;
    if (currentStep < steps.length) {
        adjustment = steps[currentStep];
    }
    var p = pitch + adjustment;

    var on = new NoteOn;
    on.pitch = p;
    on.send();
    var off = new NoteOff;
    off.pitch = p;
    off.sendAfterBeats(0.9);    
}

function ProcessMIDI() {
    var info = GetTimingInfo();
    if (!info.playing) return;
    if (startBeat < 0) return;
    var beat = info.blockStartBeat;
    if (beat - lastBeat >= 1) {
        currentStep++;
        doNote(startPitch);
        lastBeat = beat;
    }
}

2 个答案:

答案 0 :(得分:0)

您在首选项&gt;音频&gt;高级设置中设置的I / O缓冲区大小是多少?缓冲区大小越小,所需的CPU就越多。我假设您使用MainStage进行实时使用,因此您的设置非常低,可以最大限度地减少延迟。

我尝试使用缓冲区大小为16运行代码,并且MS结束并最大化CPU。 64处理得更好(CPU仪表飙升,但没有任何打嗝播放)。在2009款MacBook Pro 3.06 Core 2 Duo上测试过。

为了让Scripter顺利运行,您可能需要更多延迟。你的代码本身就很可靠。

答案 1 :(得分:0)

MainStage 3.0.3问题随之消失,我可以使用TimeMachine找到它。

我确实创建了一个自定义的Javascript实现,它的时间为3.0.4,这确实有所帮助,但是额外的100行代码(下面)。如果其他人遇到这个问题,我会建议降级。

/**---[lib/TimingInfo.js]---*/
/**
  * Constructor accepts the tempo for beat syncing and starts the clock
  * to the current time.
  *
  * @param tempo the tempo of the song (default: 120bpm)
  */
function TimingInfo(tempo) {    
    this.schedule = [];
    this.setTempo(tempo);
    this.reset();
}
/** 
  * Sets the tempo and computes all times associated, assuming a 4/4
  * beat structure.
  * 
  * @param tempo the current tempo in beats per minute.
  */
TimingInfo.prototype.setTempo = function(tempo) {
    this.tempo = tempo || 120;      
    this.msecPerBeat = (60 / this.tempo) * 1000;
};
/**
  * Resets the beatsync to be relative to the current time.
  */
TimingInfo.prototype.reset = function() {
    this.startTime = new Date().getTime();
    this.update();
    // trigger all scheduled messages immediately.  TODO: note off only?
    this._sendScheduled(null);
};
/**
  * Uses the current time to update what the current beat is and all
  * related properties.  Any scheduled actions are performed if their
  * time has passed.
  */
TimingInfo.prototype.update = function() {
    var now = new Date().getTime();
    this.elapsedMsec = now - this.startTime;
    this.beat = this.elapsedMsec / this.msecPerBeat;
    this._sendScheduled(this.beat); 
};
/**
  * Schedules a midi message to be sent at a specific beat.
  * @param e MIDI event to schedule
  * @param beat the beat number to send it on
  */
TimingInfo.prototype.sendAtBeat = function(e, beat) {
    if (e == null) return;
    // insert in-order into schedule
    var insertAt = 0;
    for (var i = 0; i < this.schedule.length; i++) {
        if (this.schedule[i].beat > beat) {
            insertAt = i;
            break;
        }
    }
    this.schedule.splice(insertAt, 0, {e:e, beat:beat});
};
/**
  * Schedules a midi message relative to current beat.
  */
TimingInfo.prototype.sendAfterBeats = function(e, deltaBeats) {
    this.sendAtBeat(e, this.beat + deltaBeats);
};
/**
  * Sends all messages scheduled on or before a given beat.  If not
  * supplied all scheduled items are sent.
  *
  * @param atBeat beat to compare all scheduled events against (default: all)
  */ 
TimingInfo.prototype._sendScheduled = function(atBeat) {
    // send all items on or before the given beat
    var sent = 0;
    for (var i = 0; i < this.schedule.length; i++) {
        if (!atBeat || this.schedule[i].beat <= atBeat) {
            this.schedule[i].e.send();
            sent++;
        }
        else {
            break;
        }
    }
    // remove sent items
    this.schedule.splice(0, sent);
};

var _timing = null;
/**
  * Replacement for GetTimingInfo() that calls update() to handling 
  * any scheduled actions.
  */
function GetNewTimingInfo() {
    if (_timing == null) {
        _timing = new TimingInfo();
    }
    _timing.update();
    return _timing;
}