1import { Invoke } from './utils.mjs';
2import { Pattern, getEventOffsetMs, noteToMidi } from '@strudel/core';
3
4const ON_MESSAGE = 0x90;
5const OFF_MESSAGE = 0x80;
6const CC_MESSAGE = 0xb0;
7
8Pattern.prototype.midi = function (output) {
9  return this.onTrigger((hap, currentTime, cps, targetTime) => {
10    let { note, nrpnn, nrpv, ccn, ccv, velocity = 0.9, gain = 1 } = hap.value;
11    //magic number to get audio engine to line up, can probably be calculated somehow
12    const latencyMs = 34;
13    const offset = getEventOffsetMs(targetTime, currentTime) + latencyMs;
14    velocity = Math.floor(gain * velocity * 100);
15    const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10);
16    const roundedOffset = Math.round(offset);
17    const midichan = (hap.value.midichan ?? 1) - 1;
18    const requestedport = output ?? 'IAC';
19    const messagesfromjs = [];
20    if (note != null) {
21      const midiNumber = typeof note === 'number' ? note : noteToMidi(note);
22      messagesfromjs.push({
23        requestedport,
24        message: [ON_MESSAGE + midichan, midiNumber, velocity],
25        offset: roundedOffset,
26      });
27      messagesfromjs.push({
28        requestedport,
29        message: [OFF_MESSAGE + midichan, midiNumber, velocity],
30        offset: roundedOffset + duration,
31      });
32    }
33    if (ccv && ccn) {
34      if (typeof ccv !== 'number' || ccv < 0 || ccv > 1) {
35        throw new Error('expected ccv to be a number between 0 and 1');
36      }
37      if (!['string', 'number'].includes(typeof ccn)) {
38        throw new Error('expected ccn to be a number or a string');
39      }
40      const scaled = Math.round(ccv * 127);
41      messagesfromjs.push({
42        requestedport,
43        message: [CC_MESSAGE + midichan, ccn, scaled],
44        offset: roundedOffset,
45      });
46    }
47    // invoke is temporarily blocking, run in an async process
48    if (messagesfromjs.length) {
49      setTimeout(() => {
50        Invoke('sendmidi', { messagesfromjs });
51      });
52    }
53  });
54};