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