FreeWRL / FreeX3D 4.3.0
Component_MIDI.c
1/*
2
3
4X3D MIDI Experimental Component 2023
5- relies on libremidi https://github.com/jcelerier/libremidi
6-- which uses C++ 17, so may need a wrapper lib_midi
7- which is a close derivitive of https://www.music.mcgill.ca/~gary/rtmidi/index.html rtmidi
8*/
9
10/****************************************************************************
11 This file is part of the FreeWRL/FreeX3D Distribution.
12
13 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
14
15 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
16 it under the terms of the GNU Lesser Public License as published by
17 the Free Software Foundation, either version 3 of the License, or
18 (at your option) any later version.
19
20 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
27****************************************************************************/
28
29
30
31#include <config.h>
32#include <system.h>
33#include <display.h>
34#include <internal.h>
35
36#include <libFreeWRL.h>
37
38#include "../vrml_parser/Structs.h"
39#include "../main/headers.h"
40
41#include "LinearAlgebra.h"
42#ifdef HAVE_LIBREMIDI
43#include "../../libmidi/libmidi.h"
44#else
45typedef struct icset { int p; int d; int ld; int n; int s; int ls; } icset;
46#endif
47
48typedef struct pComponent_MIDI {
49 Stack* midi_context_stack;
50 Stack* midi_parent_stack;
51}*ppComponent_MIDI;
52void* Component_MIDI_constructor() {
53 void* v = MALLOCV(sizeof(struct pComponent_MIDI));
54 memset(v, 0, sizeof(struct pComponent_MIDI));
55 return v;
56}
57void Component_MIDI_init(struct tComponent_MIDI* t) {
58 //public
59 //t->sound_from_audioclip = 0;
60
61 //private
62 t->prv = Component_MIDI_constructor();
63 {
64 ppComponent_MIDI p = (ppComponent_MIDI)t->prv;
65 p->midi_context_stack = newStack(int);
66 stack_push(int, p->midi_context_stack, 0); //a null will signal we have no audio context yet.
67 p->midi_parent_stack = newStack(icset);
68 icset aps = { 0, 0, 0, 0, 0, 0 };
69 stack_push(icset, p->midi_parent_stack, aps); //a null will signal we have no audio parent yet.
70
71 }
72}
73void Component_MIDI_clear(struct tComponent_MIDI* t) {
74 ppComponent_MIDI p = (ppComponent_MIDI)t->prv;
75 //deleteVector(struct X3D_Node*, p->audio_context_stack);
76}
77//ppComponent_MIDI p = (ppComponent_MIDI)gglobal()->Component_MIDI.prv;
78
79
80#ifdef HAVE_LIBREMIDI
81
82/*
83struct X3D_MidiRep {
84 int itype; //==8, 0 PointRep 1 LineRep 2 PolyRep 3 MeshRep 4 TextureRep 5 LightRep 6 ProjectorRep 7 SoundRep 8 MidiRep
85 int icontext; //map audio_contexts[icontext] = libmidi context
86 int inode; //map nodes[inode] = libmidi node
87 unsigned int iframe; //last frame visited on scenegraph traversal
88 int ibuffer; //just for source nodes with a buffer, like MIDIFileSource
89 void* connections;
90 int last_indexSource[10];
91 int last_indexDestination[10];
92 int last_count;
93};
94*/
95struct X3D_MidiRep* getMidiRep(struct X3D_Node* pnode) {
96 //main benefit of _intern Rep structure: saves switch-casing on _NodeType
97 // to get specific common fields used for internal processing only
98 // -- just put the common fields in the Rep
99 // in our case it would be our lookup table int, maybe some AudioNode fields related to connecting, starting, stopping
100 struct X3D_MidiRep* srep = NULL;
101 if (pnode) {
102 srep = (struct X3D_MidiRep*)pnode->_intern;
103 if (!srep) {
104 srep = (struct X3D_MidiRep*)malloc(sizeof(struct X3D_MidiRep));
105 memset(srep, 0, sizeof(struct X3D_MidiRep));
106 srep->itype = 8; //MidiRep
107 pnode->_intern = (struct X3D_GeomRep*)srep;
108 }
109 }
110 return srep;
111}
112
113void register_visit_check(struct X3D_Node* node);
114void visit_check_midi(struct X3D_Node* node, unsigned int iframe) {
115 struct X3D_MidiRep* srep = getMidiRep(node);
116 if (srep->icontext) {
117 if (iframe == srep->iframe) {
118 libmidi_resumeContext0(srep->icontext);
119 //libmidi_resumeNode0(node);
120 }
121 else {
122 //not visited on last frame, perhaps in a switch deactivated branch
123 //lets pause the context
124 libmidi_pauseContext0(srep->icontext);
125 //libmidi_pauseNode0(node);
126 }
127 }
128}
129// v4 visibility functions, push & pop (to be) called from all X3DGroupingNode child_ functions
130void push_midi_context(int midi_context) {
131 ppComponent_MIDI p = (ppComponent_MIDI)gglobal()->Component_MIDI.prv;
132 stack_push(int, p->midi_context_stack, midi_context);
133}
134void create_and_push_midi_context(struct X3D_Node* node) {
135 //Hypothesis: Destination / output audio nodes create a context, and child source and processing audio nodes use the context
136 //Semi-disconfirmed for MIDI. Source nodes have different threads, but shouldn't affect connections.
137 //H2: no need for a context - just make connections whereever.
138 struct X3D_MidiRep* srep = getMidiRep(node);
139 if (!srep->icontext) {
140 int jcontext = peek_midi_context();
141 if (!jcontext) {
142 jcontext = libmidi_createContext0();
143 }
144 srep->icontext = jcontext;
145 register_visit_check(node); //mainloop will check for you if it visits a node on a frame
146 }
147 push_midi_context(srep->icontext);
148}
149void pop_midi_context() {
150 ppComponent_MIDI p = (ppComponent_MIDI)gglobal()->Component_MIDI.prv;
151 stack_pop(int, p->midi_context_stack);
152}
153int peek_midi_context() {
154 ppComponent_MIDI p = (ppComponent_MIDI)gglobal()->Component_MIDI.prv;
155 return stack_top(int, p->midi_context_stack);
156}
157void push_midi_parent(int inode) {
158 ppComponent_MIDI p = (ppComponent_MIDI)gglobal()->Component_MIDI.prv;
159 icset aps = { 0, 0, 0, 0, 0, 0 };
160 aps.p = inode;
161 aps.d = 0;
162 aps.ld = 0;
163 stack_push(icset, p->midi_parent_stack, aps);
164}
165void pop_midi_parent() {
166 ppComponent_MIDI p = (ppComponent_MIDI)gglobal()->Component_MIDI.prv;
167 stack_pop(icset, p->midi_parent_stack);
168}
169icset peek_midi_parent() {
170 ppComponent_MIDI p = (ppComponent_MIDI)gglobal()->Component_MIDI.prv;
171 return stack_top(icset, p->midi_parent_stack);
172}
173
174int midinewconnect(struct X3D_MidiRep* srep, icset iparent) {
175 if (!srep->connections) srep->connections = newStack(ivec3);
176 int duplicate = 0;
177 for (int i = 0; i < vectorSize(srep->connections); i++) {
178 icset conn = vector_get(icset, srep->connections, i);
179 if (conn.p == iparent.p && conn.n == iparent.n && conn.d == iparent.d && conn.s == iparent.s)
180 duplicate = 1;
181 }
182 if (!duplicate) {
183 stack_push(icset, srep->connections, iparent);
184 }
185 return 1 - duplicate;
186}
187int mididisconnect(struct X3D_MidiRep* srep, icset iparent) {
188 if (iparent.d == iparent.ld && iparent.s == iparent.ls) return 0; //no change in destination or source
189 if (!srep->connections) return 0; //no connections yet to delete
190 int found_old = -1;
191 for (int i = 0; i < vectorSize(srep->connections); i++) {
192 icset conn = vector_get(icset, srep->connections, i);
193 // the problem is initializing on first render only, so non-zero ls = s, ld = d
194 if (conn.p == iparent.p && conn.n == iparent.n) {
195 if (iparent.d != iparent.ld && conn.d == iparent.ld)
196 if (conn.s == iparent.s || conn.s == iparent.ls) found_old = i; //WHAT IF BOTH DESTINATION AND SOURCE CHANGE ON SAME FRAME?
197 if (iparent.s != iparent.ls && conn.s == iparent.ls)
198 if (conn.d == iparent.d || conn.d == iparent.ld) found_old = i; //WHAT IF BOTH DESTINATION AND SOURCE CHANGE ON SAME FRAME?
199 }
200 }
201 if (found_old > -1) {
202 vector_remove_elem(icset, srep->connections, found_old);
203 }
204 return found_old > -1 ? 1 : 0;
205}
206void update_midi_connections(struct X3D_MidiRep* srep, icset iparent)
207{
208 if (midinewconnect(srep, iparent)) {
209 libmidi_connect(srep->icontext, iparent);
210 libmidi_print_connections();
211 }
212 if (mididisconnect(srep, iparent)) {
213 libmidi_disconnect(srep->icontext, iparent);
214 libmidi_print_connections();
215 }
216}
217
218void render_MIDIPortSource(struct X3D_MIDIPortSource* node) {
219 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
220 srep->iframe = gglobal()->Mainloop.iframe;
221 struct X3D_Node* anode = (struct X3D_Node*)node;
222 icset iparent = peek_midi_parent();
223
224 //if (node->_ichange != node->_change) {
225 if (TRUE) {
226 //if (node->_ichange == 0) return;
227 int icontext = peek_midi_context();
228 libmidi_updateNode3(icontext, iparent, anode);
229 //MARK_NODE_COMPILED
230 node->_ichange = node->_change;
231 node->_ichange++; //come in here every loop
232 //could MARK_EVENT outputs
233 }
234 iparent.n = srep->inode;
235 iparent.s = 0;
236 update_midi_connections(srep, iparent);
237
238}
239enum {
240 LOADER_INITIAL_STATE = 0,
241 LOADER_REQUEST_RESOURCE,
242 LOADER_FETCHING_RESOURCE,
243 LOADER_PROCESSING,
244 LOADER_LOADED,
245 LOADER_COMPILED,
246 LOADER_STABLE,
247};
248void compile_MIDIFileSource(struct X3D_MIDIFileSource* node) {
249 resource_item_t* res;
250 int retval = FALSE;
251
252 switch (node->__loadstatus) {
253 case LOADER_INITIAL_STATE: /* nothing happened yet */
254
255 if (node->url.n == 0) {
256 node->__loadstatus = LOADER_STABLE; /* a "do-nothing" approach */
257 }
258 else {
259 res = resource_create_multi(&(node->url));
260 res->media_type = resm_midi; //resm_fshader;
261 node->__loadstatus = LOADER_REQUEST_RESOURCE;
262 node->__loadResource = res;
263 }
264 break;
265
266 case LOADER_REQUEST_RESOURCE:
267 res = node->__loadResource;
268 resource_identify(node->_parentResource, res);
269 /* printf ("load_Inline, we have type %s status %s\n",
270 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
271 res->actions = resa_download | resa_load; //not resa_parse which we do below
272 resitem_enqueue(ml_new(res));
273 //frontenditem_enqueue(ml_new(res));
274 node->__loadstatus = LOADER_FETCHING_RESOURCE;
275 break;
276
277 case LOADER_FETCHING_RESOURCE:
278 res = node->__loadResource;
279 /* printf ("load_Inline, we have type %s status %s\n",
280 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
281 // do we try the next url in the multi-url?
282 if (res->complete) {
283 if (res->status == ress_loaded) {
284 node->__loadstatus = LOADER_STABLE;
285 openned_file_t* of;
286 of = res->openned_files;
287 if (!of) {
288 /* error */
289 return;
290 }
291 node->__loadstatus = LOADER_LOADED;
292 node->__blob.p = (int*)of->fileData;
293 node->__blob.n = of->fileDataSize;
294 }
295 else if ((res->status == ress_failed) || (res->status == ress_invalid)) {
296 //no hope left
297 printf("resource failed to load\n");
298 node->__loadstatus = LOADER_STABLE; // a "do-nothing" approach
299 }
300 }
301 break;
302
303 case LOADER_PROCESSING:
304 res = node->__loadResource;
305
306 //printf ("inline parsing.... %s\n",resourceStatusToString(res->status));
307 printf("res complete %d\n", res->complete);
308 if (res->complete) {
309 if (res->status == ress_parsed) {
310 node->__loadstatus = LOADER_LOADED;
311 }
312 else {
313 node->__loadstatus = LOADER_STABLE;
314 }
315 }
316
317 break;
318 case LOADER_STABLE:
319 break;
320 case LOADER_LOADED:
321 case LOADER_COMPILED:
322 retval = TRUE;
323 }
324 if (node->__loadstatus == LOADER_STABLE || node->__loadstatus == LOADER_LOADED)
325 MARK_NODE_COMPILED
326}
327
328void render_MIDIFileSource(struct X3D_MIDIFileSource* node) {
329 COMPILE_IF_REQUIRED;
330 //if (node->__loadstatus == LOADER_LOADED) printf("loaded ");
331 if (node->__loadstatus != LOADER_LOADED) return;
332
333 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
334 srep->iframe = gglobal()->Mainloop.iframe;
335 struct X3D_Node* anode = (struct X3D_Node*)node;
336 icset iparent = peek_midi_parent();
337 if (!srep->ibuffer) {
338 //start a thread to parse the blob
339 printf("loaded .mid file size = %d\n", node->__blob.n);
340 srep->ibuffer = node->__blob.n;
341 int icontext = peek_midi_context();
342 libmidi_updateNode3(icontext, iparent, anode);
343 }
344
345 //if (node->_ichange != node->_change) {
346 if(TRUE){
347 //if (node->_ichange == 0) return;
348 int icontext = peek_midi_context();
349 libmidi_updateNode3(icontext, iparent, anode);
350 //MARK_NODE_COMPILED
351 node->_ichange = node->_change;
352 node->_ichange++; //come in here every loop
353 //could MARK_EVENT outputs
354 }
355 iparent.n = srep->inode;
356 iparent.s = 0;
357 update_midi_connections(srep, iparent);
358
359}
360void render_MIDIPortDestination(struct X3D_MIDIPortDestination* node) {
361 struct X3D_Node* anode = (struct X3D_Node*)node;
362 icset have_parent = peek_midi_parent();
363 create_and_push_midi_context(anode);
364 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
365 libmidi_updateNode3(peek_midi_context(), have_parent, anode);
366 if (!have_parent.p) {
367 push_midi_parent(srep->inode); // 1); //should be the audio context device node
368 icset junk = peek_midi_parent();
369 }
370 srep->iframe = gglobal()->Mainloop.iframe;
371 if (node->children.n) {
372 for (int i = 0; i < node->children.n; i++)
373 render_node(X3D_NODE(node->children.p[i]));
374 }
375 if (!have_parent.p)
376 pop_midi_parent(); //audio context device node 1
377 //libmidi_print_connections();
378 pop_midi_context();
379
380}
381void render_MIDIPrintDestination(struct X3D_MIDIPrintDestination* node) {
382 struct X3D_Node* anode = (struct X3D_Node*)node;
383 icset have_parent = peek_midi_parent();
384 create_and_push_midi_context(anode);
385 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
386 libmidi_updateNode3(peek_midi_context(), have_parent, anode);
387 if (!have_parent.p) {
388 push_midi_parent(srep->inode); // 1); //should be the audio context device node
389 }
390 srep->iframe = gglobal()->Mainloop.iframe;
391 if (node->children.n) {
392 for (int i = 0; i < node->children.n; i++)
393 render_node(X3D_NODE(node->children.p[i]));
394 }
395 if (!have_parent.p)
396 pop_midi_parent(); //audio context device node 1
397 //libmidi_print_connections();
398 pop_midi_context();
399
400}
401void render_MIDIFileDestination(struct X3D_MIDIFileDestination* node) {
402 if (node->children.n) {
403 for (int i = 0; i < node->children.n; i++)
404 render_node(X3D_NODE(node->children.p[i]));
405 }
406
407}
408void render_MIDIOut(struct X3D_MIDIOut* node) {
409 struct X3D_Node* anode = (struct X3D_Node*)node;
410 icset have_parent = peek_midi_parent();
411 create_and_push_midi_context(anode);
412 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
413 libmidi_updateNode3(peek_midi_context(), have_parent, anode);
414 if (!have_parent.p) {
415 push_midi_parent(srep->inode); // 1); //should be the audio context device node
416 }
417 srep->iframe = gglobal()->Mainloop.iframe;
418 if (node->children.n) {
419 for (int i = 0; i < node->children.n; i++)
420 render_node(X3D_NODE(node->children.p[i]));
421 }
422 if (!have_parent.p)
423 pop_midi_parent(); //audio context device node 1
424 pop_midi_context();
425}
426void render_MIDIIn(struct X3D_MIDIIn* node) {
427 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
428 srep->iframe = gglobal()->Mainloop.iframe;
429 struct X3D_Node* anode = (struct X3D_Node*)node;
430 icset iparent = peek_midi_parent();
431
432 if (node->_ichange != node->_change) {
433 //if (TRUE) {
434 int icontext = peek_midi_context();
435 libmidi_updateNode3(icontext, iparent, anode);
436 MARK_NODE_COMPILED
437 }
438 iparent.n = srep->inode;
439 iparent.s = 0;
440 update_midi_connections(srep, iparent);
441}
442void render_MIDIProgram(struct X3D_MIDIProgram* node) {
443 struct X3D_Node* anode = (struct X3D_Node*)node;
444 icset iparent = peek_midi_parent();
445 create_and_push_midi_context(anode);
446 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
447 libmidi_updateNode3(peek_midi_context(), iparent, anode);
448 if (!iparent.p) {
449 push_midi_parent(srep->inode); // 1); //should be the audio context device node
450 }
451 srep->iframe = gglobal()->Mainloop.iframe;
452 if (node->children.n) {
453 push_midi_parent(srep->inode);
454 for (int i = 0; i < node->children.n; i++)
455 render_node(X3D_NODE(node->children.p[i]));
456 pop_midi_parent();
457 }
458 iparent.n = srep->inode;
459 iparent.s = 0;
460 update_midi_connections(srep, iparent);
461
462 if (!iparent.p)
463 pop_midi_parent(); //audio context device node 1
464 pop_midi_context();
465}
466void render_MIDIDelay(struct X3D_MIDIDelay* node) {
467 struct X3D_Node* anode = (struct X3D_Node*)node;
468 icset iparent = peek_midi_parent();
469 create_and_push_midi_context(anode);
470 struct X3D_MidiRep* srep = getMidiRep(X3D_NODE(node));
471 libmidi_updateNode3(peek_midi_context(), iparent, anode);
472 if (!iparent.p) {
473 push_midi_parent(srep->inode); // 1); //should be the audio context device node
474 }
475 srep->iframe = gglobal()->Mainloop.iframe;
476 if (node->children.n) {
477 push_midi_parent(srep->inode);
478 for (int i = 0; i < node->children.n; i++)
479 render_node(X3D_NODE(node->children.p[i]));
480 pop_midi_parent();
481 }
482 iparent.n = srep->inode;
483 iparent.s = 0;
484 update_midi_connections(srep, iparent);
485
486 if (!iparent.p)
487 pop_midi_parent(); //audio context device node 1
488 pop_midi_context();
489}
490/*
491enum message_type
492{
493 INVALID = 0x0,
494 // Standard Message
495 NOTE_OFF = 0x80,
496 NOTE_ON = 0x90,
497 POLY_PRESSURE = 0xA0,
498 CONTROL_CHANGE = 0xB0,
499 PROGRAM_CHANGE = 0xC0,
500 AFTERTOUCH = 0xD0,
501 PITCH_BEND = 0xE0,
502
503 // System Common Messages
504 SYSTEM_EXCLUSIVE = 0xF0,
505 TIME_CODE = 0xF1,
506 SONG_POS_POINTER = 0xF2,
507 SONG_SELECT = 0xF3,
508 RESERVED1 = 0xF4,
509 RESERVED2 = 0xF5,
510 TUNE_REQUEST = 0xF6,
511 EOX = 0xF7,
512
513 // System Realtime Messages
514 TIME_CLOCK = 0xF8,
515 RESERVED3 = 0xF9,
516 START = 0xFA,
517 CONTINUE = 0xFB,
518 STOP = 0xFC,
519 RESERVED4 = 0xFD,
520 ACTIVE_SENSING = 0xFE,
521 SYSTEM_RESET = 0xFF
522};
523*/
524//typedef unsigned char ubyte;
525// MIDI 1 MESSAGES (packed in SFInt32)
526void midimsg_uint2values(unsigned int msg, ubyte* channel, ubyte* command, ubyte* note, ubyte* velocity) {
527 //we don't care about big endian etc just unpack opposite of packing
528 //we are assuming the msg is a 3 byte note on/off, may need if/else on command for others
529 ubyte* bytes = (ubyte*)&msg;
530 *channel = (bytes[0] & 0xF) + 1;
531 *command = bytes[0] - (*channel - 1);
532 *note = bytes[1];
533 *velocity = bytes[2];
534}
535unsigned int midimsg_values2uint(ubyte channel, ubyte command, ubyte note, ubyte velocity) {
536 unsigned int msg;
537 ubyte* bytes = (ubyte*)&msg;
538 bytes[0] = (channel - 1) | command;
539 bytes[1] = note;
540 bytes[2] = velocity;
541 return msg;
542}
543
544// MIDI 2.0 UMP PACKETS (packed in SFDouble)
545void midiump_packet2values(double packet, ubyte* channel, ubyte* command, ubyte* note, ushort* velocity) {
546 //we don't care about big endian etc just unpack opposite of packing
547 UMP ump;
548 ump.packet = packet;
549 *channel = (ump.bytes[1] & 0xF) + 1;
550 *command = ump.bytes[1] - (*channel - 1);
551 *note = ump.bytes[2];
552 *velocity= ump.u16[2];
553}
554double midiump_values2packet(ubyte channel, ubyte command, ubyte note, ushort velocity) {
555 UMP ump;
556 ump.bytes[1] = (channel - 1) | command;
557 ump.bytes[2] = note;
558 ump.u16[2] = velocity;
559 return ump.packet;
560}
561
562void render_MIDIConverterOut(struct X3D_MIDIConverterOut* node) {}
563void render_MIDIConverterIn(struct MIDIConverterIn* node) {}
564void offset_notefields_MidiToneSplitter(size_t* cfield) {
565 cfield[0] = (offsetof(struct X3D_MIDIToneSplitter, C));
566 cfield[1] = (offsetof(struct X3D_MIDIToneSplitter, Cs));
567 cfield[2] = (offsetof(struct X3D_MIDIToneSplitter, D));
568 cfield[3] = (offsetof(struct X3D_MIDIToneSplitter, Ds));
569 cfield[4] = (offsetof(struct X3D_MIDIToneSplitter, E));
570 cfield[5] = (offsetof(struct X3D_MIDIToneSplitter, F));
571 cfield[6] = (offsetof(struct X3D_MIDIToneSplitter, Fs));
572 cfield[7] = (offsetof(struct X3D_MIDIToneSplitter, G));
573 cfield[8] = (offsetof(struct X3D_MIDIToneSplitter, Gs));
574 cfield[9] = (offsetof(struct X3D_MIDIToneSplitter, A));
575 cfield[10] = (offsetof(struct X3D_MIDIToneSplitter, As));
576 cfield[11] = (offsetof(struct X3D_MIDIToneSplitter, B));
577 cfield[12] = (offsetof(struct X3D_MIDIToneSplitter, pedal));
578
579}
580void render_MIDIToneSplitter(struct X3D_MIDIToneSplitter* node) {
581 static size_t cfields[13] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0 };
582 if (cfields[0] == 0) offset_notefields_MidiToneSplitter(cfields);
583
584 struct X3D_Node* anode = X3D_NODE(node);
585 if (node->_ichange != node->_change) {
586 if (MIDITransport() == MIDI_MSG) {
587 for (int i = 0; i < node->midiMsg.n; i++)
588 {
589 ubyte note, channel, command, velocity;
590 unsigned int msg = (unsigned int)node->midiMsg.p[i];
591 midimsg_uint2values(msg, &channel, &command, &note, &velocity);
592 int status = command == NOTE_ON && velocity > 0 ? TRUE : FALSE;
593 int octave = note / 12;
594 int inote = note % 12;
595 if (channel == node->channelFilter || node->channelFilter == -1) {
596 if (command == NOTE_ON || command == NOTE_OFF) {
597 if (octave == node->octaveFilter || node->octaveFilter == -1)
598 {
599 //printf("inote = %d cfields %zu\n", inote,cfields[inote]);
600 int* field = (int*)(cfields[inote] + (unsigned char*)node); //fancy offsetof to eliminate switch-case
601 *field = status;
602 MARK_EVENT(anode, cfields[inote]);
603 }
604 }
605 else if (command == CONTROL_CHANGE && note == 64) {
606 node->pedal = velocity == 0 ? FALSE : TRUE;
607 MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, pedal));
608 }
609 }
610 }
611 }
612 else if (MIDITransport() == MIDI_UMP) {
613 for (int i = 0; i < node->midiUmp.n; i++)
614 {
615 ubyte note, channel, command;
616 ushort velocity;
617 UMP ump;
618 ump.packet = node->midiUmp.p[i];
619 midiump_packet2values(ump.packet, &channel, &command, &note, &velocity);
620 int status = command == NOTE_ON && velocity > 0 ? TRUE : FALSE;
621 int octave = note / 12;
622 int inote = note % 12;
623 if (channel == node->channelFilter || node->channelFilter == -1) {
624 if (command == NOTE_ON || command == NOTE_OFF) {
625 if (octave == node->octaveFilter || node->octaveFilter == -1)
626 {
627 //printf("inote = %d cfields %zu\n", inote,cfields[inote]);
628 int* field = (int*)(cfields[inote] + (unsigned char*)node); //fancy offsetof to eliminate switch-case
629 *field = status;
630 MARK_EVENT(anode, cfields[inote]);
631 }
632 }
633 else if (command == CONTROL_CHANGE && note == 64) {
634 node->pedal = velocity == 0 ? FALSE : TRUE;
635 MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, pedal));
636 }
637 }
638 }
639 }
640
641 /*
642 switch (inote) {
643 case 0: node->C = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, C)); break;
644 case 1: node->Cs = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, Cs)); break;
645 case 2: node->D = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, D)); break;
646 case 3: node->Ds = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, Ds)); break;
647 case 4: node->E = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, E)); break;
648 case 5: node->F = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, F)); break;
649 case 6: node->Fs = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, Fs)); break;
650 case 7: node->G = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, G)); break;
651 case 8: node->Gs = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, Gs)); break;
652 case 9: node->A = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, A)); break;
653 case 10: node->As = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, As)); break;
654 case 11: node->B = status; MARK_EVENT(anode, offsetof(struct X3D_MIDIToneSplitter, B)); break;
655 default: break;
656 }
657 */
658 MARK_NODE_COMPILED
659
660 }
661}
662void offset_notefields_MidiToneMerger(size_t * cfield) {
663 cfield[0] = (offsetof(struct X3D_MIDIToneMerger, C));
664 cfield[1] = (offsetof(struct X3D_MIDIToneMerger, Cs));
665 cfield[2] = (offsetof(struct X3D_MIDIToneMerger, D));
666 cfield[3] = (offsetof(struct X3D_MIDIToneMerger, Ds));
667 cfield[4] = (offsetof(struct X3D_MIDIToneMerger, E));
668 cfield[5] = (offsetof(struct X3D_MIDIToneMerger, F));
669 cfield[6] = (offsetof(struct X3D_MIDIToneMerger, Fs));
670 cfield[7] = (offsetof(struct X3D_MIDIToneMerger, G));
671 cfield[8] = (offsetof(struct X3D_MIDIToneMerger, Gs));
672 cfield[9] = (offsetof(struct X3D_MIDIToneMerger, A));
673 cfield[10] = (offsetof(struct X3D_MIDIToneMerger, As));
674 cfield[11] = (offsetof(struct X3D_MIDIToneMerger, B));
675 cfield[12] = (offsetof(struct X3D_MIDIToneMerger, pedal));
676}
677void render_MIDIToneMerger(struct X3D_MIDIToneMerger* node) {
678 struct X3D_Node* anode = X3D_NODE(node);
679 //static struct Multi_Bool lastnote;
680 static size_t cfields[13]; //reserve last one for pedal
681 if (node->_lastnote.n == 0)
682 {
683 //we store last frame's note on/off values, so we can detect if something changed
684 node->_lastnote.p = malloc(13 * sizeof(int));
685 node->_lastnote.n = 13;
686 for (int i = 0; i < 13; i++) node->_lastnote.p[i] = FALSE;
687 offset_notefields_MidiToneMerger(cfields);
688 }
689 if (node->_ichange != node->_change) {
690 int n, mark, mnote[12]; //max polyphony 12,assume fixed octave and max 12 changed notes in octave per frame
691 double dnote[12];
692 n = 0;
693 mark = FALSE;
694 for(int i=0;i<node->_lastnote.n;i++)
695 {
696 int lastval = node->_lastnote.p[i];
697 int octave = node->octave;
698 int channel = node->channel;
699 int inote = i;
700 int note = i + 12 * octave;
701 int *field = (int*)(cfields[i] + (unsigned char*)node); //fancy offsetof to eliminate switch-case
702 int curval = *field;
703 if(curval != lastval) {
704 ubyte command;
705 if (i < 12) {
706 command = curval ? NOTE_ON : NOTE_OFF;
707 }
708 else {
709 command = CONTROL_CHANGE;
710 note = 64; //pedal?
711 }
712 if (MIDITransport() == MIDI_MSG){
713 ubyte velocity = curval ? 127 : 0;
714 mnote[n] = midimsg_values2uint(channel, command, note, velocity);
715 }
716 if (MIDITransport() == MIDI_UMP) {
717 ushort velocity = curval ? 65535 : 0;
718 dnote[n] = midiump_values2packet(channel, command, note, velocity);
719 }
720 n++;
721 //mnote[n] = curval ? note : -note; n++;
722 mark = TRUE;
723 }
724 node->_lastnote.p[i] = curval;
725 /*
726 if (octave == node->octave )
727 {
728 switch (inote) {
729 case 0: if (node->C != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; } break;
730 case 1: if (node->Cs != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; } break;
731 case 2: if (node->D != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; } break;
732 case 3: if (node->Ds != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
733 case 4: if (node->E != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
734 case 5: if (node->F != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
735 case 6: if (node->Fs != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
736 case 7: if (node->G != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
737 case 8: if (node->Gs != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
738 case 9: if (node->A != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
739 case 10: if (node->As != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; } break;
740 case 11: if (node->B != status) { mnote[n] = node->C ? note : -note; n++; mark = TRUE; }break;
741 default: break;
742 }
743 }
744 */
745 }
746 if (mark) {
747 if (MIDITransport() == MIDI_MSG) {
748 // MIDI 1 messages packed into MFInt32
749 node->midiMsg.p = realloc(node->midiMsg.p, n * sizeof(int));
750 memcpy(node->midiMsg.p, mnote, n * sizeof(int));
751 node->midiMsg.n = n;
752 MARK_EVENT(anode, offsetof(struct X3D_MIDIToneMerger, midiMsg));
753 }
754 if (MIDITransport() == MIDI_UMP) {
755 // MIDI 2 packets packed into MFDouble
756 node->midiUmp.p = realloc(node->midiUmp.p, n * sizeof(double));
757 memcpy(node->midiUmp.p, dnote, n * sizeof(double));
758 node->midiUmp.n = n;
759 MARK_EVENT(anode, offsetof(struct X3D_MIDIToneMerger, midiUmp));
760 }
761 }
762 MARK_NODE_COMPILED
763
764 }
765
766}
767void render_MIDIAudioSynth(struct MIDIAudioSynth* node) {}
768
769static midi_transport_method = MIDI_UMP;
770void set_MIDITransport(int method) {
771 midi_transport_method = method == 1 || method == 2 ? method : midi_transport_method;
772}
773int MIDITransport() {
774 return midi_transport_method; // MIDI_UMP;
775 //return MIDI_MSG;
776}
777#else //HAVE_LIBREMIDI
778//stubs
779void compile_MIDIFileSource(struct X3D_MIDIFileSource* node) {}
780void render_MIDIPortSource(struct X3D_MIDIPortSource* node) {}
781void render_MIDIFileSource(struct X3D_MIDIFileSource* node) {}
782void render_MIDIPortDestination(struct X3D_MIDIPortDestination* node) {}
783void render_MIDIPrintDestination(struct X3D_MIDIPrintDestination* node) {}
784void render_MIDIFileDestination(struct X3D_MIDIFileDestination* node) {}
785void render_MIDIOut(struct X3D_MIDIOut* node) {}
786void render_MIDIIn(struct X3D_MIDIIn* node) {}
787void render_MIDIProgram(struct X3D_MIDIProgram* node) {}
788void render_MIDIDelay(struct X3D_MIDIDelay* node) {}
789void render_MIDIConverterOut(struct X3D_MIDIConverterOut* node) {}
790void render_MIDIConverterIn(struct MIDIConverterIn* node) {}
791void render_MIDIToneSplitter(struct MIDIToneSplitter* node) {}
792void render_MIDIToneMerger(struct MIDIToneMerger* node) {}
793void render_MIDIAudioSynth(struct MIDIAudioSynth* node) {}
794
795#endif //HAVE_LIBREMIDI
Definition libmidi.h:3
Definition libmidi.h:18