1 /*
2     midiout.c:
3 
4     Copyright (C) 1997 Gabriel Maldonado, John ffitch
5 
6     This file is part of Csound.
7 
8     The Csound Library is free software; you can redistribute it
9     and/or modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2.1 of the License, or (at your option) any later version.
12 
13     Csound is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU Lesser General Public License for more details.
17 
18     You should have received a copy of the GNU Lesser General Public
19     License along with Csound; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21     02110-1301 USA
22 */
23 
24 /****************************************/
25 /** midiout UGs by Gabriel Maldonado   **/
26 /****************************************/
27 
28 /* Some modifications by JPff for general use */
29 
30 #include <math.h>
31 #include "csoundCore.h"
32 #include "midiout.h"
33 
34 #define MGLOB(x) (((CSOUND*)csound)->midiGlobals->x)
35 
36 #define NUMCHN          (16)
37 #define EXTRA_TIME      (1)
38 
39 extern void openMIDIout(CSOUND *);
40 /* static MYFLT   invkr; */
41 void note_on(CSOUND *, int32_t chan, int32_t num, int32_t vel);
42 void note_off(CSOUND *, int32_t chan, int32_t num, int32_t vel);
43 void control_change(CSOUND *, int32_t chan, int32_t num, int32_t value);
44 void after_touch(CSOUND *, int32_t chan, int32_t value);
45 void program_change(CSOUND *, int32_t chan, int32_t num);
46 void pitch_bend(CSOUND *, int32_t chan, int32_t lsb, int32_t msb);
47 void poly_after_touch(CSOUND *, int32_t chan, int32_t note_num, int32_t value);
48 void send_midi_message(CSOUND *, int32_t status, int32_t data1, int32_t data2);
49 
release_set(CSOUND * csound,REL * p)50 int32_t release_set(CSOUND *csound, REL *p)
51 {
52     IGN(csound);
53     if (p->h.insdshead->xtratim < EXTRA_TIME)
54       /* if not initialised by another opcode */
55       p->h.insdshead->xtratim = EXTRA_TIME;
56     return OK;
57 }
58 
release(CSOUND * csound,REL * p)59 int32_t release(CSOUND *csound, REL *p)
60 {
61     IGN(csound);
62     if (p->h.insdshead->relesing)
63       *p->r = FL(1.0); /* TRUE */
64     else
65       *p->r = FL(0.0); /* FALSE */
66     return OK;
67 }
68 
xtratim(CSOUND * csound,XTRADUR * p)69 int32_t xtratim(CSOUND *csound, XTRADUR *p)
70 {
71     IGN(csound);
72     int32_t *xtra = &(p->h.insdshead->xtratim);
73     int32_t tim = (int32_t)(*p->extradur * p->h.insdshead->ekr);
74     if (*xtra < tim)  /* gab-a5 revised */
75       *xtra = tim;
76     return OK;
77 }
78 
mclock_set(CSOUND * csound,MCLOCK * p)79 int32_t mclock_set(CSOUND *csound, MCLOCK *p)
80 {
81     IGN(csound);
82     p->period= CS_EKR / *p->freq;
83     p->clock_tics = p->period;
84     p->beginning_flag = TRUE;
85     return OK;
86 }
87 
mclock(CSOUND * csound,MCLOCK * p)88 int32_t mclock(CSOUND *csound, MCLOCK *p)
89 {
90     if (UNLIKELY(p->beginning_flag)) {    /* first time */
91       send_midi_message(csound, 0xF8, 0, 0);    /* clock message */
92       p->beginning_flag=FALSE;
93       return OK;
94     }
95     else if ((MYFLT) CS_KCNT > p->clock_tics) {
96       send_midi_message(csound, 0xF8, 0, 0);    /* clock message */
97       p->clock_tics += p->period;
98     }
99     return OK;
100 }
101 
mrtmsg(CSOUND * csound,MRT * p)102 int32_t mrtmsg(CSOUND *csound, MRT *p)
103 {
104     switch ((int32_t)*p->message) {
105     case 0:
106       send_midi_message(csound, 0xFC, 0, 0); /* stop */
107       break;
108     case 1:
109       send_midi_message(csound, 0xFA, 0, 0); /* start */
110       break;
111     case 2:
112       send_midi_message(csound, 0xFB, 0, 0); /* continue */
113       break;
114     case -1:
115       send_midi_message(csound, 0xFF, 0, 0); /* system_reset */
116       break;
117     case -2:
118       send_midi_message(csound, 0xFE, 0, 0); /* active_sensing */
119       break;
120     default:
121       return csound->InitError(csound, Str("illegal mrtmsg argument"));
122     }
123     return OK;
124 }
125 
iout_on(CSOUND * csound,OUT_ON * p)126 int32_t iout_on(CSOUND *csound, OUT_ON *p)
127 {
128     note_on(csound, (int32_t)*p->ichn-1,(int32_t)*p->inum,(int32_t)*p->ivel);
129     return OK;
130 }
131 
iout_off(CSOUND * csound,OUT_ON * p)132 int32_t iout_off(CSOUND *csound, OUT_ON *p)
133 {
134     note_off(csound, (int32_t)*p->ichn-1,(int32_t)*p->inum,(int32_t)*p->ivel);
135     return OK;
136 }
137 
iout_on_dur_set(CSOUND * csound,OUT_ON_DUR * p)138 int32_t iout_on_dur_set(CSOUND *csound, OUT_ON_DUR *p)
139 {
140     int32_t temp;
141     if (p->h.insdshead->xtratim < EXTRA_TIME)
142       /* if not initialised by another opcode */
143       p->h.insdshead->xtratim = EXTRA_TIME;
144 
145     p->chn = (temp = abs((int32_t)*p->ichn-1)) < NUMCHN ? temp : NUMCHN-1;
146     p->num = (temp = abs((int32_t)*p->inum)) < 128 ? temp : 127;
147     p->vel = (temp = abs((int32_t)*p->ivel)) < 128 ? temp : 127;
148 
149     note_on(csound, p->chn,p->num, p->vel);
150     p->istart_time = (MYFLT)CS_KCNT * CS_ONEDKR;
151     p->fl_expired = FALSE;
152     p->fl_extra_dur = FALSE;
153     return OK;
154 }
155 
iout_on_dur(CSOUND * csound,OUT_ON_DUR * p)156 int32_t iout_on_dur(CSOUND *csound, OUT_ON_DUR *p)
157 {
158     if (!(p->fl_expired)) {
159       MYFLT actual_dur = (MYFLT) CS_KCNT * CS_ONEDKR
160                          - p->istart_time;
161       MYFLT dur = *p->idur;
162       if (dur < actual_dur) {
163         p->fl_expired = TRUE;
164         note_off(csound, p->chn, p->num, p->vel);
165       }
166       else if (p->h.insdshead->relesing) {
167         p->fl_expired = TRUE;
168         note_off(csound, p->chn, p->num, p->vel);
169       }
170     }
171     return OK;
172 }
173 
iout_on_dur2(CSOUND * csound,OUT_ON_DUR * p)174 int32_t iout_on_dur2(CSOUND *csound, OUT_ON_DUR *p)
175 {
176     if (!(p->fl_expired)) {
177       MYFLT actual_dur = (MYFLT)CS_KCNT * CS_ONEDKR
178                          - p->istart_time;
179       MYFLT dur = *p->idur;
180       if (dur < actual_dur) {
181         p->fl_expired = TRUE;
182         note_off(csound, p->chn, p->num, p->vel);
183       }
184       else if (p->h.insdshead->relesing || p->fl_extra_dur) {
185 
186         if (!p->fl_extra_dur && dur > actual_dur) {
187 
188           p->h.insdshead->offtim +=  dur - actual_dur+ FL(1.0);
189           p->h.insdshead->relesing =0;
190           p->fl_extra_dur=TRUE;
191         }
192         else if (dur <= actual_dur) {
193           note_off(csound, p->chn, p->num, p->vel);
194         }
195       }
196     }
197     return OK;
198 }
199 
moscil_set(CSOUND * csound,MOSCIL * p)200 int32_t moscil_set(CSOUND *csound, MOSCIL *p)
201 {
202     IGN(csound);
203     if (p->h.insdshead->xtratim < EXTRA_TIME)
204       /* if not initialised by another opcode */
205       p->h.insdshead->xtratim = EXTRA_TIME;
206     p->istart_time = (MYFLT)CS_KCNT * CS_ONEDKR;
207     p->fl_first_note   = TRUE;
208     p->fl_note_expired = TRUE;
209     p->fl_end_note     = FALSE;
210     return OK;
211 }
212 
moscil(CSOUND * csound,MOSCIL * p)213 int32_t moscil(CSOUND *csound, MOSCIL *p)
214 {
215     if (p->fl_first_note) {
216       p->fl_first_note = FALSE;
217       goto first_note;
218     }
219     if (!(p->fl_note_expired)) {
220       if (p->h.insdshead->relesing) {
221         p->fl_note_expired = TRUE;
222         p->fl_end_note     = TRUE;
223         note_off(csound, p->last_chn, p->last_num, p->last_vel);
224       }
225       else if (p->last_dur < (MYFLT)CS_KCNT * CS_ONEDKR
226                              - p->istart_time) {
227         p->fl_note_expired = TRUE;
228         note_off(csound, p->last_chn, p->last_num, p->last_vel);
229       }
230     }
231     else {
232       if (!p->fl_end_note
233           && p->last_pause + p->last_dur <
234              (MYFLT)CS_KCNT * CS_ONEDKR - p->istart_time
235           && !(p->h.insdshead->relesing)) {
236         MYFLT ftemp;
237         p->istart_time = p->istart_time + p->last_pause + p->last_dur;
238         p->last_dur   =         /* dur must be at least 1/kr */
239           (ftemp = *p->kdur) > 0 ? ftemp : CS_ONEDKR;
240         p->last_pause = (ftemp = *p->kpause) > 0 ? ftemp : CS_ONEDKR;
241  first_note:
242         {
243           int32_t temp;
244           p->last_chn = (temp = abs((int32_t)*p->kchn-1)) < NUMCHN ? temp :
245                                                                   NUMCHN-1;
246           p->last_num = (temp = abs((int32_t)*p->knum)) < 128    ? temp : 127;
247           p->last_vel = (temp = abs((int32_t)*p->kvel)) < 128    ? temp : 127;
248         }
249         p->fl_note_expired = FALSE;
250         note_on(csound, p->last_chn, p->last_num, p->last_vel);
251       }
252     }
253     return OK;
254 }
255 
kvar_out_on_set(CSOUND * csound,KOUT_ON * p)256 int32_t kvar_out_on_set(CSOUND *csound, KOUT_ON *p)
257 {
258     IGN(csound);
259     if (p->h.insdshead->xtratim < EXTRA_TIME)
260       /* if not initialised by another opcode */
261       p->h.insdshead->xtratim = EXTRA_TIME;
262     p->fl_first_note = TRUE;
263     return OK;
264 }
265 
kvar_out_on(CSOUND * csound,KOUT_ON * p)266 int32_t kvar_out_on(CSOUND *csound, KOUT_ON *p)
267 {
268     if (p->fl_first_note) {
269       int32_t temp;
270 
271       p->last_chn = (temp = abs((int32_t)*p->kchn-1)) < NUMCHN  ? temp : NUMCHN-1;
272       p->last_num = (temp = abs((int32_t)*p->knum)) < 128     ? temp : 127;
273       p->last_vel = (temp = abs((int32_t)*p->kvel)) < 128     ? temp : 127;
274       p->fl_first_note   = FALSE;
275       p->fl_note_expired = FALSE;
276 
277       note_on(csound, p->last_chn, p->last_num, p->last_vel);
278     }
279     else if (p->fl_note_expired) return OK;
280     else {
281       if (p->h.insdshead->relesing) {
282         note_off(csound, p->last_chn, p->last_num, p->last_vel);
283         p->fl_note_expired = TRUE;
284       }
285       else {
286         int32_t tt;
287         int32_t curr_chn = (tt = abs((int32_t)*p->kchn-1)) < NUMCHN ? tt : NUMCHN-1;
288         int32_t curr_num = (tt = abs((int32_t)*p->knum)) < 128    ? tt : 127;
289         int32_t curr_vel = (tt = abs((int32_t)*p->kvel)) < 128    ? tt : 127;
290 
291         if (  p->last_chn != curr_chn
292                || p->last_num != curr_num
293                || p->last_vel != curr_vel
294               ) {
295           note_off(csound, p->last_chn, p->last_num, p->last_vel);
296 
297           p->last_chn = curr_chn;
298           p->last_num = curr_num;
299           p->last_vel = curr_vel;
300 
301           note_on(csound, curr_chn, curr_num, curr_vel);
302         }
303       }
304     }
305     return OK;
306 }
307 
out_controller(CSOUND * csound,OUT_CONTR * p)308 int32_t out_controller (CSOUND *csound, OUT_CONTR *p)
309 {
310     /* if (!(p->h.insdshead->prvinstance)) JPff/VL */ {
311       /* if prev instance already allocated in the same MIDI chan */
312       int32_t value;
313       MYFLT min = *p->min;
314       value =  (int32_t)((*p->value - min) * FL(127.0) / (*p->max - min));
315       value = (value < 128) ?  value : 127;
316       value = (value > -1) ?  value : 0;
317       if (value != p->last_value ||
318           *p->chn != p->lastchn ||
319           *p->num != p->lastctrl) {
320         /* csound->Message(csound, "out contr value: %d\n", value); */
321         control_change(csound, (int32_t)*p->chn-1,(int32_t)*p->num ,value);
322         p->last_value = value;
323         p->lastchn = *p->chn;
324         p->lastctrl = *p->num;
325         }
326     }
327     return OK;
328 }
329 
out_aftertouch(CSOUND * csound,OUT_ATOUCH * p)330 int32_t out_aftertouch (CSOUND *csound, OUT_ATOUCH *p)
331 {
332     /* if (!(p->h.insdshead->prvinstance)) JPff/VL */ {
333       /* if prev instance already allocated in the same MIDI chan */
334       int32_t value;
335       MYFLT min = *p->min;
336       value =  (int32_t)((*p->value - min) * FL(127.0) / (*p->max - min));
337       value = value < 128 ?  value : 127;
338       value = value > -1  ?  value : 0;
339       if (value != p->last_value || *p->chn != p->lastchn) {
340         after_touch(csound, (int32_t)*p->chn-1, value);
341         p->last_value = value;
342         p->lastchn = *p->chn;
343        }
344     }
345     return OK;
346 }
347 
out_poly_aftertouch(CSOUND * csound,OUT_POLYATOUCH * p)348 int32_t out_poly_aftertouch (CSOUND *csound, OUT_POLYATOUCH *p)
349 {
350     int32_t value;
351     MYFLT min = *p->min;
352     value =  (int32_t)((*p->value - min) * FL(127.0) / (*p->max - min));
353     value = value < 128 ?  value : 127;
354     value = value > -1  ?  value : 0;
355     if (value != p->last_value ||
356         *p->chn != p->lastchn  ||
357         *p->num != p->lastctrl) {
358       poly_after_touch(csound, (int32_t)*p->chn-1, (int32_t)*p->num, value);
359       p->last_value = value;
360       p->lastchn = *p->chn;
361       p->lastctrl = *p->num;
362 }
363 
364     return OK;
365 }
366 
out_progchange(CSOUND * csound,OUT_PCHG * p)367 int32_t out_progchange (CSOUND *csound, OUT_PCHG *p)
368 {
369     /* if (!(p->h.insdshead->prvinstance)) JPff/VL */ {
370       /* if prev instance already allocated in the same MIDI chan */
371       int32_t prog_num;
372       MYFLT min = *p->min;
373       prog_num =  (int32_t)((*p->prog_num - min) * FL(127.0) / (*p->max - min));
374       prog_num = prog_num < 128 ?  prog_num : 127;
375       prog_num = prog_num > -1  ?  prog_num : 0;
376       if (prog_num != p->last_prog_num || *p->chn != p->lastchn) {
377         program_change(csound, (int32_t)*p->chn-1, prog_num);
378         p->last_prog_num = prog_num;
379         p->lastchn = *p->chn;
380         }
381     }
382     return OK;
383 }
384 
out_controller14(CSOUND * csound,OUT_CONTR14 * p)385 int32_t out_controller14 (CSOUND *csound, OUT_CONTR14 *p)
386 {
387     /* if (!(p->h.insdshead->prvinstance)) JPff/VL */ {
388       /* if prev instance already allocated in the same MIDI chan */
389       int32_t value;
390       MYFLT min = *p->min;
391 
392       value =  (int32_t)((*p->value - min) * FL(16383.0) / (*p->max - min));
393       value = (value < 16384) ?  value : 16383;
394       value = (value > -1) ?  value : 0;
395 
396       if (value != p->last_value  ||
397           *p->chn != p->lastchn   ||
398           *p->msb_num != p->lastctrl) {
399         uint32_t msb = value >> 7;
400         uint32_t lsb = value & 0x7F;
401         csound->Warning(csound, Str("out contr14 msb:%x lsb:%x\n"), msb, lsb);
402         control_change(csound, (int32_t)*p->chn-1, (int32_t)*p->msb_num, msb);
403         control_change(csound, (int32_t)*p->chn-1, (int32_t)*p->lsb_num, lsb);
404         p->last_value = value;
405         p->lastchn = *p->chn;
406         p->lastctrl = *p->msb_num;
407          }
408     }
409     return OK;
410 }
411 
out_pitch_bend(CSOUND * csound,OUT_PB * p)412 int32_t out_pitch_bend(CSOUND *csound, OUT_PB *p)
413 {
414     /* if (p->h.insdshead->prvinstance) { */
415     /*   /\* if prev instance already allocated in the same MIDI chan *\/ */
416     /*   return OK; */
417     /* } */
418     /* JPff/VL else */ {
419       int32_t   value;
420       MYFLT min = *p->min;
421 
422       value = (int32_t)((*p->value - min) * FL(16383.0) / (*p->max - min));
423       value = (value < 16384  ?  value : 16383);
424       value = (value > -1     ?  value : 0);
425       if (value != p->last_value || *p->chn != p->lastchn )  {
426         uint32_t msb = value >> 7;
427         uint32_t lsb = value & 0x7F;
428         pitch_bend(csound, (int32_t)*p->chn - 1, lsb, msb);
429         p->last_value = value;
430         p->lastchn = *p->chn;
431         }
432     }
433     return OK;
434 }
435 
kon2_set(CSOUND * csound,KON2 * p)436 int32_t kon2_set(CSOUND *csound, KON2 *p)
437 {
438    IGN(csound);
439     /* if not initialised by another opcode */
440     if (p->h.insdshead->xtratim < EXTRA_TIME)
441       p->h.insdshead->xtratim = EXTRA_TIME;
442     /*p->fl_first_note = TRUE;*/
443     p->fl_note_expired = FALSE;
444 
445     return OK;
446 }
447 
kon2(CSOUND * csound,KON2 * p)448 int32_t kon2(CSOUND *csound, KON2 *p)
449 {
450     /*
451         if (p->fl_first_note) {
452           register int32_t tt;
453 
454           p->last_chn = (tt = abs((int32_t)*p->kchn)) < NUMCHN  ? tt : NUMCHN-1;
455           p->last_num = (tt = abs((int32_t)*p->knum)) < 128     ? tt : 127;
456           p->last_vel = (tt = abs((int32_t)*p->kvel)) < 128     ? tt : 127;
457           p->fl_first_note   = FALSE;
458           p->fl_note_expired = FALSE;
459 
460           note_on(csound, p->last_chn, p->last_num, p->last_vel);
461         }
462 
463     */
464     /*else */
465     if (p->fl_note_expired) return OK;
466     else {
467       if (p->h.insdshead->relesing) {
468         note_off(csound, p->last_chn,p->last_num,p->last_vel);
469         p->fl_note_expired = TRUE;
470       }
471       else {
472         int32_t tt;
473 
474         int32_t curr_chn = (tt = abs((int32_t)*p->kchn-1)) <= NUMCHN ? tt : NUMCHN;
475         int32_t curr_num = (tt = abs((int32_t)*p->knum)) < 128    ? tt : 127;
476         int32_t curr_vel = (tt = abs((int32_t)*p->kvel)) < 128    ? tt : 127;
477 
478         if ((int32_t)(*p->ktrig +FL(0.5)) != 0  )/* i.e. equal to 1  */
479                                 /*   p->last_chn != curr_chn
480                                 || p->last_num != curr_num
481                                 || p->last_vel != curr_vel
482                                 ) */
483           {
484             note_off(csound, p->last_chn, p->last_num, p->last_vel);
485             p->last_chn = curr_chn;
486             p->last_num = curr_num;
487             p->last_vel = curr_vel;
488             note_on(csound, curr_chn, curr_num, curr_vel);
489           }
490       }
491     }
492     return OK;
493 }
494 
midiout(CSOUND * csound,MIDIOUT * p)495 int32_t midiout(CSOUND *csound, MIDIOUT *p)         /*gab-A6 fixed*/
496 {
497     int32_t st, ch, d1, d2;
498 
499     st = (int32_t)(*p->in_type + FL(0.5));
500     if (!st)
501       return OK;
502     st = (st & 0x70) | 0x80;
503     ch = (int32_t)(*p->in_chan - FL(0.5)) & 0x0F;
504     d1 = (int32_t)(*p->in_dat1 + FL(0.5)) & 0x7F;
505     d2 = (int32_t)(*p->in_dat2 + FL(0.5)) & 0x7F;
506     send_midi_message(csound, st | ch, d1, d2);
507     return OK;
508 }
509 
nrpn(CSOUND * csound,NRPN * p)510 int32_t nrpn(CSOUND *csound, NRPN *p)
511 {
512     int32_t chan = (int32_t)*p->chan-1, parm = (int32_t)*p->parm_num;
513     int32_t value = (int32_t)*p->parm_value;
514     if (chan != p->old_chan || parm != p->old_parm || value != p->old_value) {
515       int32_t status = 176 | chan;
516       int32_t parm_msb =  parm >> 7;
517       int32_t parm_lsb =  parm  & 0x7f;
518 
519       int32_t value_msb = (value + 8192) >> 7;
520       int32_t value_lsb = (value + 8192) % 128;
521 
522       send_midi_message(csound, status, 99, parm_msb);
523       send_midi_message(csound, status, 98, parm_lsb);
524       send_midi_message(csound, status,  6, value_msb);
525       send_midi_message(csound, status, 38, value_lsb);
526       p->old_chan = chan;
527       p->old_parm = parm;
528       p->old_value = value;
529     }
530     return OK;
531 }
532 
mdelay_set(CSOUND * csound,MDELAY * p)533 int32_t mdelay_set(CSOUND *csound, MDELAY *p)
534 {
535    IGN(csound);
536     p->read_index = 0;
537     p->write_index = 0;
538     memset(p->status, 0, DELTAB_LENGTH);
539     return OK;
540 }
541 
mdelay(CSOUND * csound,MDELAY * p)542 int32_t mdelay(CSOUND *csound, MDELAY *p)                   /*gab-A6 fixed*/
543 {
544     int32_t read_index = p->read_index % DELTAB_LENGTH;
545     int32_t write_index = p->write_index % DELTAB_LENGTH;
546     MYFLT present_time =  CS_KCNT * CS_ONEDKR;
547 
548     if (((int32_t)*p->in_status == 0x90 || (int32_t)*p->in_status == 0x80)) {
549       p->status[write_index] = (int32_t)*p->in_status;
550       p->chan[write_index] = (int32_t)*p->in_chan-1;
551       p->dat1[write_index] = (int32_t)*p->in_dat1;
552       p->dat2[write_index] = (int32_t)*p->in_dat2;
553       p->time[write_index] = present_time;
554       (p->write_index)++;
555     }
556     if (p->status[read_index] &&
557         p->time[read_index] + *p->kdelay <= present_time) {
558       int32_t number = p->dat1[read_index];
559       int32_t velocity = p->dat2[read_index];
560       send_midi_message(csound,  p->status[read_index] | p->chan[read_index],
561                                  ((number   > 127) ? 127 : number),
562                                  ((velocity > 127) ? 127 : velocity));
563       (p->read_index)++;
564     }
565     return OK;
566 }
567