FreeWRL / FreeX3D 4.3.0
libmidi.cpp
1
2
3/* part of libfreewrl, usual libfreewrl license
4not currently building this as a lib,
5-- just putting libmidi.cpp in libfreewrl list of source files
6-- so could be in /lib/scenegraph
7- called by Component_MIDI.c
8General design:
9- similar to web audio, midi nodes will be implicitly connected by scenegraph hierarchy
10- a separate thread will be recursing from MIDIsource through list of registered listener nodes
11-- to MIDI destination or MIDI utility node that converts to ROUTEs or to web audio sound node
12- the rendering thread will visit once per frame, scrape the latest data
13-- and send it as field updates / routes or whatever.
14- relies on libremidi https://github.com/jcelerier/libremidi
15-- which uses C++ 17, so needs this wrapper libmidi
16- libremidi is a close derivitive of rtmidi https://www.music.mcgill.ca/~gary/rtmidi/index.html
17-- rtmidi is a close 2nd choice / fallback, and apparently has an optional C interface
18*/
19#ifdef HAVE_LIBREMIDI
20#define __STDC_LIMIT_MACROS
21#include <chrono>
22#include <cstdlib>
23#include <iostream>
24#include <libremidi/libremidi.hpp>
25#include <libremidi/reader.hpp>
26#include <thread> //https://en.cppreference.com/w/cpp/thread/thread
27#include <mutex>
28#include <condition_variable>
29#include <map>
30#include <queue>
31
32static libremidi::midi_in midiin;
33static libremidi::midi_out midiout;
34
35extern "C" {
36 void midiin_C_callback(const libremidi::message * msg);
37}
38void midiin_callback(libremidi::message& msg)
39{
40 midiin_C_callback(&msg);
41}
42void set_midiin_callback() {
43 std::cout << "setting input callback" << std::endl;
44 midiin.set_callback(
45 [](const libremidi::message& message)
46 {
47 /*
48 std::cout << "input callback called " << std::endl;
49 std::vector<unsigned char> messout(message.size());
50 auto nBytes = message.size();
51 for (auto i = 0U; i < nBytes; i++)
52 std::cout << "Byte " << i << " = " << (int)message[i] << ", ";
53 if (nBytes > 0)
54 std::cout << "stamp = " << message.timestamp << std::endl;
55 messout[0] = message[0];
56 messout[1] = message[1];
57 messout[2] = message[2];
58 if (messout[2] > 0 && messout[2] < 64)
59 messout[2] = 64;
60 midiout.send_message(messout);
61 */
62 midiin_C_callback(&message);
63
64 });
65
66}
67// A threadsafe-queue. https://stackoverflow.com/questions/15278343/c11-thread-safe-queue
68template <class T>
69class SafeQueue
70{
71public:
72 SafeQueue(void)
73 : q()
74 , m()
75 , c()
76 {}
77
78 ~SafeQueue(void)
79 {}
80
81 // Add an element to the queue.
82 void enqueue(T t)
83 {
84 std::lock_guard<std::mutex> lock(m);
85 q.push(t);
86 c.notify_one();
87 }
88 bool empty() {
89 if (q.empty()) return true;
90 return false;
91 }
92 // Get the "front"-element.
93 // If the queue is empty, wait till a element is avaiable.
94 T dequeue(void)
95 {
96 std::unique_lock<std::mutex> lock(m);
97 //if (q.empty()) return 0.0; // nullptr;
98 //while (q.empty())
99 //{
100 // // release lock as long as the wait and reaquire it afterwards.
101 // c.wait(lock);
102 //}
103 T val = q.front();
104 q.pop();
105 return val;
106 }
107
108private:
109 std::queue<T> q;
110 mutable std::mutex m;
111 std::condition_variable c;
112};
113
114//make the interface flat C
115#ifdef __cplusplus
116extern "C" {
117#endif
118#define TRUE 1
119#define FALSE 0
120
121#define GLDOUBLE double
122typedef struct {
123 void* p; /* Pointer to actual object */
124 unsigned int x; /* Extra information - reuse count etc */
126
127typedef ptw32_handle_t pthread_t;
128#include "../lib/vrml_parser/Structs.h"
129#include "libmidi.h"
130typedef unsigned short ushort;
131void midiump_packet2values(double packet, ubyte* channel, ubyte* command, ubyte* note, ushort* velocity);
132double TickTime();
133
134typedef struct MidiNode {
135 int itype;
136 int numberOfInputs;
137 int numberOfOutputs;
138 //std::list<MidiNode> inputs;
139 std::list<MidiNode*> outputs;
140 void (*takemessage)(MidiNode*, const struct libremidi::message *);
141 void (*takepacket)(MidiNode*, timedpacket packet);
142 libremidi::reader* reader;
143 int run;
144 int loop;
145 void* queue;
146 int instrument, last_instrument;
147 double delay,starttime,lasttime;
148} MidiNode;
149struct mcstruct {
150 //std::thread context;
151 //bool running;
152 int next_node;
153 //int next_bus;
154 std::map<int, MidiNode*> nodes;
155 std::map<int, int> nodetype;
156 };
157static int next_midi_context = 0;
158static std::map<int, struct mcstruct*> midi_contexts;
159//void midi_context_function(struct mcstruct* ac) {
160// while (true) {
161// if (!ac->running)
162// std::this_thread::sleep_for(std::chrono::seconds(1));
163// else {
164//
165// }
166// }
167//}
168int libmidi_createContext0() {
169 // midi needs one context I think
170 // yes there are separate threads for each source
171 // but that shouldn't affect connections
172 if (next_midi_context == 0) {
173 next_midi_context++;
174
175 struct mcstruct *ac = new mcstruct();
176
177 midi_contexts[next_midi_context] = ac;
178 //ac->context = std::thread(midi_context_function, ac);
179 //ac->running = FALSE;
180 }
181 return next_midi_context;
182}
183
184// from midi.org MIDI 2.0 Protocol
185// M2-104-UM_v1-1_UMP_and_MIDI_2-0_Protocol_Specification.pdf
186// Appendix D: Translation: MIDI 1.0 and MIDI 2.0 Messages
187// July 2023 we don't have Microsoft.Devices.Midi2.Core package available
188// -- so can't build for Midi 2 services (yet, coming eventually)
189// but we can translate MIDI 1 port and file messages to UMP universal midi packet format
190// and translate back at port destination
191// so web3d MIDI processing nodes will be in UMP / Midi2 format, and midi UMP packets will be routed
192// scene designers can parse and make UMP packets in SAI javascript
193// -- they'll get a 64 bit SFDouble or SFTime field/event in UMP format
194// and when Midi2 services arrive, it will be just the Source and Destination nodes that change.
195// 2 possible designs:
196// a) take and make note-specific messages (command, note, velocity)
197// b) general message to/from packet conversion (Appendix D)
198// below we do the general conversion so we are ready for MIDI2.
199
200// from Appendix D
201// Min-Center-Max Upscaling Algorithm
202// power of 2, pow(2, exp)
203uint32_t power_of_2(uint8_t exp)
204{
205 return 1 << exp; // implement integer power of 2 using bit shift
206}
207// preconditions: srcBits > 1, dstBits<=32, srcBits < dstBits
208uint32_t scaleUp(uint32_t srcVal, uint8_t srcBits, uint8_t dstBits)
209{
210 uint8_t scaleBits = (dstBits - srcBits); // number of bits to upscale
211 uint32_t srcCenter = power_of_2(srcBits - 1); // center value for srcBits, e.g.
212 // 0x40 (64) for 7 bits
213 // 0x2000 (8192) for 14 bits
214 // simple bit shift
215 uint32_t bitShiftedValue = srcVal << scaleBits;
216 if (srcVal <= srcCenter) {
217 return bitShiftedValue;
218 }
219 // expanded bit repeat scheme
220 uint8_t repeatBits = srcBits - 1; // we must repeat all but the highest bit
221 uint32_t repeatMask = power_of_2(repeatBits) - 1;
222 uint32_t repeatValue = srcVal & repeatMask; // repeat bit sequence
223 if (scaleBits > repeatBits) { // need to repeat multiple times
224 repeatValue <<= (scaleBits - repeatBits);
225 }
226 else {
227 repeatValue >>= (repeatBits - scaleBits);
228 }
229 while (repeatValue != 0) {
230 bitShiftedValue |= repeatValue; // fill lower bits with repeatValue
231 repeatValue >>= repeatBits; // move repeat bit sequence to next position
232 }
233 return bitShiftedValue;
234}
235// Code for Min - Center - Max Scaling Up from 7 - Bit to 16 - Bit
236uint16_t scaleUp7to16(uint8_t value7) {
237 uint16_t bitShiftedValue = (uint16_t)value7 << 9;
238 if (value7 <= 64) {
239 return bitShiftedValue;
240 }
241 // use bit repeat bits from extended value7
242 uint16_t repeatValue6 = (uint16_t)value7 & 0x3F;
243 return bitShiftedValue
244 | (repeatValue6 << 3)
245 | (repeatValue6 >> 3);
246}
247
248//typedef union {
249// double packet;
250// unsigned short u16[4];
251// unsigned char bytes[8];
252//} UMP;
253int msg2ump(int nbytes, const unsigned char* bytes, double* packets, int fixvelocity) {
254 //a long message can produce multiple packets
255 //nbytes - number of msg bytes
256 //bytes - msg bytes
257 //packets - array of UMP packets, please dimension double packets[100] in calling code
258 //return - number of packets (usually 1, or 0 if no mapping to midi2, but might be nultple with long message)
259 printf("in msg2ump\n");
260 UMP ump;
261 ump.packet = 0.0;
262 int npacket = 0;
263 ubyte channel, note, command, velocity7;
264 channel = (bytes[0] & 0xF);
265 command = bytes[0] - channel;
266 note = bytes[1];
267 velocity7 = bytes[2];
268 //code to convert bytes to packets
269 //D.3.1 Not On/Off
270 if (command == NOTE_ON || command == NOTE_OFF){
271 ump.bytes[1] = bytes[0]; //command, channel
272 ump.bytes[2] = bytes[1]; //note
273 ump.u16[3] = 0; //velocity
274 if (command == NOTE_ON) {
275 if(velocity7 == 0)
276 ump.bytes[1] = channel | NOTE_OFF;
277 else {
278 if (fixvelocity && velocity7 < 64) velocity7 = 63;
279 ump.u16[2] = scaleUp7to16(bytes[2]);
280 }
281 }
282 packets[npacket] = ump.packet;
283 npacket++;
284 }
285 //D.3.2 PolyPressure
286 if (command == POLY_PRESSURE) {
287 ump.bytes[1] = bytes[0]; //command, channel
288 ump.bytes[2] = bytes[1]; //note
289 ump.u32[1] = scaleUp(bytes[2], 7, 32);
290 packets[npacket] = ump.packet;
291 npacket++;
292 }
293 //D.3.3 Control Change, RPN, and NRPN
294 if (command == CONTROL_CHANGE) {
295 ump.bytes[1] = bytes[0];
296 ump.bytes[2] = bytes[1];
297 ump.u32[1] = scaleUp(bytes[2], 7, 32);
298 packets[npacket] = ump.packet;
299 npacket++;
300 //special case with MSB, LSB values, don't know if I have the right idea
301 //would we be getting a long message, or 2 messages? If 2, then buffer
302 if (nbytes > 3 && bytes[2] > 0 && bytes[2] < 32 && bytes[3] > 0 && bytes[3] < 64) {
303 //send 2nd packet
304 ump.bytes[1] = bytes[0];
305 ump.bytes[2] = bytes[1];
306 ump.u32[1] = scaleUp(bytes[3], 7, 32);
307 packets[npacket] = ump.packet;
308 npacket++;
309 }
310 }
311 //D.3.4 Program Change and Bank Select
312 if (command == PROGRAM_CHANGE) {
313 ump.bytes[1] = bytes[0];
314 ump.bytes[4] = bytes[1];
315 static ubyte bankselect_msb = 0, bankselect_lsb = 0;
316 if (bankselect_msb && bankselect_lsb) {
317 ump.bytes[6] = bankselect_msb;
318 ump.bytes[7] = bankselect_lsb;
319 }
320 packets[npacket] = ump.packet;
321 npacket++;
322
323 }
324 //D.3.5 Channel Pressure
325 if (command == CHANNEL_PRESSURE) {
326 ump.bytes[1] = bytes[0];
327 ump.u32[1] = scaleUp(bytes[1], 7, 32);
328 packets[npacket] = ump.packet;
329 npacket++;
330 }
331 //D.3.6 Pitch Bend
332 if (command == PITCH_BEND) {
333 ump.bytes[1] = bytes[0];
334 ushort pitchbend = bytes[1] | (bytes[2] << 7);
335 ump.u32[1] = scaleUp(pitchbend, 14, 32);
336 packets[npacket] = ump.packet;
337 npacket++;
338 }
339 //D.3.7 System Messages
340 if (command == SYSTEM_EXCLUSIVE) {
341 int mbytes = channel;
342 ump.bytes[1] = bytes[0]; //should the top bit be zeroed? Or whole thing?
343
344 int j = 0;
345 if (mbytes <= 6) {
346 for (int i = 0; i < mbytes; i++) {
347 if (!(bytes[i + 1] == 0xF0 || bytes[i + 1] == 0xF7)) {
348 ump.bytes[2 + j] = bytes[1 + i];
349 j++;
350 }
351 }
352 }
353 else {
354 //break into multiple packets
355
356 }
357 //ump.bytes[1] = command | j; //Am I supposed to change the count in the status? or do they not count sthe start and end bytes either
358 //what if more bytes than 6? does that ever happen? If so what then?
359 packets[npacket] = ump.packet;
360 npacket++;
361 }
362 //memcpy(ump.bytes, bytes, nbytes); //delivers midi1 messages in first 3 bytes
363 packets[0] = ump.packet;
364 return 1;
365}
366// Code for Downscaling Algorithm
367uint32_t scaleDown(uint32_t srcVal, uint8_t srcBits, uint8_t dstBits) {
368 // simple bit shift
369 uint8_t scaleBits = (srcBits - dstBits);
370 return srcVal >> scaleBits;
371}
372
373int ump2msg(double packet, ubyte **msg, int *nbytes, ubyte* bytearray) {
374 //chained packets can produce a single long message
375 //ump - packet
376 //bytes - msg bytes - please dimension unsigned char bytes[200] in calling code
377 //return: number of msg bytes (might be 0 for start of a long message)
378 // this function will buffer packets till it gets the last of chained packets
379 //static unsigned char ubytes[200];
380 //static int nbytes = 0; //one-time initializatino
381 printf("in ump2msg\n");
382 ubyte command, channel, note, mt, group, * bytes;
383 ushort velocity;
384 UMP ump;
385 ump.packet = packet;
386 int nmsg = 0;
387 bytes = bytearray;
388 midiump_packet2values(packet, &channel, &command, &note, &velocity);
389 channel--;
390 printf("ump2msg chan %d com %d note %d vel %d\n", channel, command, note, velocity);
391 mt = ump.bytes[0] >> 4;
392 group = ump.bytes[0] & (0xF >> 4);
393 //code to convert ump to bytes
394 if (command >= 0x10 && command <= 0x30) {
395 //D.2.6 System Messages
396 //D.2.7 System Exclusive
397 static ubyte cumbytes[100];
398 static int ncum = 0; //initialize once
399 int nb = channel;
400 if (ncum == 0 || command == 0x10) {
401 //start new sysex command
402 cumbytes[0] = SYSTEM_EXCLUSIVE; // = 0xF0
403 ncum++;
404 }
405 for (int i = 0; i < channel; i++) {
406 cumbytes[ncum] = ump.bytes[i + 2];
407 ncum++;
408 }
409 if (command == 0x30) {
410 //end sysex message
411 cumbytes[ncum] = EOX; //end of transmission byte
412 ncum++;
413 memcpy(bytes, cumbytes, ncum);
414 msg[nmsg] = bytes;
415 nbytes[nmsg] = ncum;
416 bytes += ncum;
417 nmsg++;
418 }
419 }
420 else {
421 //not a system exclusive
422 switch (command) {
423 //D.2.1 Not On/Off, Poly Pressure, Control Change
424 case NOTE_ON:
425 case NOTE_OFF:
426 case POLY_PRESSURE:
427 case CONTROL_CHANGE:
428 {
429 memset(bytes, 0, 3);
430 bytes[0] = command | channel | 1 << 7; //is the top bit still set?
431 bytes[1] = ump.bytes[2];
432 bytes[2] = (ubyte)scaleDown(ump.u16[2], 16, 7);
433 if (command == NOTE_ON && bytes[2] == 0) bytes[2] = 0x01; //minimum note on velocity
434 msg[nmsg] = bytes;
435 nbytes[nmsg] = 3;
436 bytes += 3;
437 nmsg++;
438 }
439 break;
440 //D.2.2 Channel Pressure
441 case CHANNEL_PRESSURE:
442 {
443 memset(bytes, 0, 2);
444 bytes[0] = command | channel | 1 << 7; //is the top bit still set?
445 bytes[1] = (ubyte)scaleDown(ump.bytes[4], 8, 7);
446 msg[nmsg] = bytes;
447 nbytes[nmsg] = 2;
448 bytes += 2;
449 nmsg++;
450 }
451 break;
452 //D.2.3 Assignable Controllers (NRPN) and Registered Controllers (RPN)
453 //D.2.4 Program Change and Bank Select
454 case PROGRAM_CHANGE:
455 {
456 if (ump.bytes[3] & 1) {
457 //bank select MSB
458 memset(bytes, 0, 3);
459 bytes[0] = command | channel | 1 << 7; //is the top bit still set?
460 bytes[2] = ump.bytes[6] & (0xF >> 1); //make sure top bit clear
461 msg[nmsg] = bytes;
462 nbytes[nmsg] = 3;
463 bytes += 3;
464 nmsg++;
465 //bank select LSB
466 memset(bytes, 0, 3);
467 bytes[0] = command | channel | 1 << 7; //is the top bit still set?
468 bytes[2] = ump.bytes[7] & (0xF >> 1); //make sure top bit clear
469 msg[nmsg] = bytes;
470 nbytes[nmsg] = 3;
471 bytes += 3;
472 nmsg++;
473
474 }
475 //unconditional program change
476 memset(bytes, 0, 2);
477 bytes[0] = ump.bytes[1]; // command | channel | 1 << 7; //is the top bit still set?
478 bytes[1] = ump.bytes[4]; // &(0xF >> 1); //make sure top bit clear
479 msg[nmsg] = bytes;
480 nbytes[nmsg] = 2;
481 bytes += 2;
482 nmsg++;
483 }
484 break;
485 case PITCH_BEND:
486 {
487 //D.2.5 Pitch Bend
488 memset(bytes, 0, 3);
489 bytes[0] = command | channel | 1 << 7; //is the top bit still set?
490 bytes[1] = (ubyte)scaleDown(ump.bytes[5], 8, 6) | ((ump.bytes[4] & 1) << 6);
491 bytes[2] = (ubyte)scaleDown(ump.bytes[4], 8, 7);
492 msg[nmsg] = bytes;
493 nbytes[nmsg] = 3;
494 bytes += 3;
495 nmsg++;
496 }
497 break;
498 //D.2.8 non-translatable to MIDI 1
499 //D.2.9 non-translatable to non-UMP MIDI 1
500 } //end switch
501 } //end else sysex
502 return nmsg;
503
504}
505
506void midifilesourcefunction(MidiNode* mnode) {
507
508 libremidi::reader* r = mnode->reader;
509
510 printf("ticks per beat %f\n", r->ticksPerBeat);
511 do {
512 for (const auto& track : r->tracks)
513 {
514 int last_tick = 0;
515 std::cout << "\nNew track\n\n";
516 for (const libremidi::track_event& event : track)
517 {
518 while (mnode->run != TRUE) std::this_thread::sleep_for(std::chrono::milliseconds(50));
519 std::cout << "Event at " << event.tick << " : ";
520 if (event.tick) {
521 //std::this_thread::sleep_for(std::chrono::milliseconds((int)((float)(event.tick - last_tick) *1000.0f / r->ticksPerBeat / 12.0f)));
522 int elapsed_ticks = event.tick - last_tick;
523 float beats_per_second = 2.0f;
524 float elapsed_seconds = ((float)elapsed_ticks / (r->ticksPerBeat * beats_per_second));
525 int elapsed_msec = (int)(elapsed_seconds * 1000.0f);
526 std::this_thread::sleep_for(std::chrono::milliseconds(elapsed_msec));
527 last_tick = event.tick;
528 }
529 if (event.m.is_meta_event())
530 {
531 std::cout << "Meta event";
532 }
533 else
534 {
535 libremidi::message msg = event.m;
536 double packets[10];
537 int npackets;
538 if(MIDITransport() == 2)
539 npackets = msg2ump(msg.bytes.size(), msg.bytes.data(), packets,FALSE);
540
541 std::wcout << "outputs.count" << mnode->outputs.size() << std::endl;
542 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
543 {
544 MidiNode* mout = *it;
545 //std::cout << "mout->takemessage=" << mout->takemessage << std::endl;
546 if (MIDITransport() == MIDI_UMP) {
547 //MIDI 2.0 UMP
548 for (int j = 0; j < npackets; j++) {
549 timedpacket tpacket;
550 tpacket.packet = packets[j];
551 tpacket.timestamp = msg.timestamp;
552 if (mout->takepacket) mout->takepacket(mout, tpacket);
553 }
554 }
555 else {
556 //MIDI 1.0 MIDI_MSG
557 if (mout->takemessage) mout->takemessage(mout, &msg);
558 }
559 }
560 if (0) switch (event.m.get_message_type())
561 {
562 case libremidi::message_type::NOTE_ON:
563 std::cout << "Note ON: "
564 << "channel " << event.m.get_channel() << ' '
565 << "note " << (int)event.m.bytes[1] << ' '
566 << "velocity " << (int)event.m.bytes[2] << ' ';
567 break;
568 case libremidi::message_type::NOTE_OFF:
569 std::cout << "Note OFF: "
570 << "channel " << event.m.get_channel() << ' '
571 << "note " << (int)event.m.bytes[1] << ' '
572 << "velocity " << (int)event.m.bytes[2] << ' ';
573 break;
574 case libremidi::message_type::CONTROL_CHANGE:
575 std::cout << "Control: "
576 << "channel " << event.m.get_channel() << ' '
577 << "control " << (int)event.m.bytes[1] << ' '
578 << "value " << (int)event.m.bytes[2] << ' ';
579 break;
580 case libremidi::message_type::PROGRAM_CHANGE:
581 std::cout << "Program: "
582 << "channel " << event.m.get_channel() << ' '
583 << "program " << (int)event.m.bytes[1] << ' ';
584 break;
585 case libremidi::message_type::AFTERTOUCH:
586 std::cout << "Aftertouch: "
587 << "channel " << event.m.get_channel() << ' '
588 << "value " << (int)event.m.bytes[1] << ' ';
589 break;
590 case libremidi::message_type::POLY_PRESSURE:
591 std::cout << "Poly pressure: "
592 << "channel " << event.m.get_channel() << ' '
593 << "note " << (int)event.m.bytes[1] << ' '
594 << "value " << (int)event.m.bytes[2] << ' ';
595 break;
596 case libremidi::message_type::PITCH_BEND:
597 std::cout << "Poly pressure: "
598 << "channel " << event.m.get_channel() << ' '
599 << "bend " << (int)(event.m.bytes[1] << 7 + event.m.bytes[2]) << ' ';
600 break;
601 default:
602 std::cout << "Unsupported.";
603 break;
604 }
605 }
606 std::cout << '\n';
607 }
608 }
609 std::cout << "end of tracks" << std::endl;
610 } while (mnode->loop == TRUE);
611 std::cout << "bye bye" << std::endl;
612}
613void midiPortDestination_takemessage(MidiNode* midiNode, const struct libremidi::message* msg) {
614 const struct libremidi::message& m = *msg;
615 std::vector<unsigned char> messout(m.size());
616 auto nBytes = m.size();
617 std::cout << "PD ";
618 for (auto i = 0U; i < nBytes; i++)
619 std::cout << "Byte " << i << " = " << (int)m[i] << ", ";
620 if (nBytes > 0)
621 std::cout << "stamp = " << m.timestamp << std::endl;
622 messout[0] = m[0];
623 messout[1] = m[1];
624 messout[2] = m[2];
625 if (messout[2] > 0 && messout[2] < 64)
626 messout[2] = 64;
627 libremidi::message mout = libremidi::message(messout,m.timestamp);
628 midiout.send_message(mout);
629
630}
631void midiPortDestination_takepacket(MidiNode* midiNode, timedpacket tpacket) {
632 //convert from UMP MIDI 2.0 to MIDI 1 message and send to output port
633 ubyte bytearray[200];
634 ubyte *msgs[50];
635 int nbytes[50];
636 double packet = tpacket.packet;
637 int nmsg = ump2msg(packet, msgs, nbytes, bytearray);
638 for(int j=0;j<nmsg;j++) {
639 ubyte* bytes = msgs[j];
640 int nbyte = nbytes[j];
641 std::vector<unsigned char> messout(nbyte);
642 for (int i = 0; i < nbyte; i++)
643 messout[i] = bytes[i];
644 libremidi::message mout = libremidi::message(messout, tpacket.timestamp);
645 midiout.send_message(mout);
646 }
647}
648void midiPrintDestination_takemessage(MidiNode* midiNode, const struct libremidi::message * msg) {
649 const struct libremidi::message& m = *msg;
650 printf("in midiPrintDestination_takemessage\n");
651 switch (m.get_message_type())
652 {
653 case libremidi::message_type::NOTE_ON:
654 std::cout << "Note ON: "
655 << "channel " << m.get_channel() << ' '
656 << "note " << (int)m.bytes[1] << ' '
657 << "velocity " << (int)m.bytes[2] << ' ';
658 break;
659 case libremidi::message_type::NOTE_OFF:
660 std::cout << "Note OFF: "
661 << "channel " << m.get_channel() << ' '
662 << "note " << (int)m.bytes[1] << ' '
663 << "velocity " << (int)m.bytes[2] << ' ';
664 break;
665 case libremidi::message_type::CONTROL_CHANGE:
666 std::cout << "Control: "
667 << "channel " << m.get_channel() << ' '
668 << "control " << (int)m.bytes[1] << ' '
669 << "value " << (int)m.bytes[2] << ' ';
670 break;
671 case libremidi::message_type::PROGRAM_CHANGE:
672 std::cout << "Program: "
673 << "channel " << m.get_channel() << ' '
674 << "program " << (int)m.bytes[1] << ' ';
675 break;
676 case libremidi::message_type::AFTERTOUCH:
677 std::cout << "Aftertouch: "
678 << "channel " << m.get_channel() << ' '
679 << "value " << (int)m.bytes[1] << ' ';
680 break;
681 case libremidi::message_type::POLY_PRESSURE:
682 std::cout << "Poly pressure: "
683 << "channel " << m.get_channel() << ' '
684 << "note " << (int)m.bytes[1] << ' '
685 << "value " << (int)m.bytes[2] << ' ';
686 break;
687 case libremidi::message_type::PITCH_BEND:
688 std::cout << "Poly pressure: "
689 << "channel " << m.get_channel() << ' '
690 << "bend " << (int)(m.bytes[1] << 7 + m.bytes[2]) << ' ';
691 break;
692 default:
693 std::cout << "Unsupported.";
694 break;
695 }
696
697 std::cout << " PrintDest\n";
698
699}
700void midiPrintDestination_takepacket(MidiNode* midiNode, timedpacket tpacket) {
701 ubyte bytearray[200];
702 ubyte* msgs[50];
703 int nbytes[50];
704 double packet = tpacket.packet;
705 printf("in midiPrintDestination_takepacket\n");
706 int nmsg = ump2msg(packet, msgs, nbytes, bytearray);
707 for(int j=0;j<nmsg;j++) {
708 ubyte *bytes = msgs[j];
709 int nbyte = nbytes[j];
710 std::vector<unsigned char> messout(nbyte);
711 for (int i = 0; i < nbyte; i++)
712 messout[i] = bytes[i];
713 libremidi::message mout = libremidi::message(messout, tpacket.timestamp);
714 midiPrintDestination_takemessage(midiNode, &mout);
715 }
716}
717void midiOut_takemessage(MidiNode* midiNode, const struct libremidi::message* msg) {
718 const struct libremidi::message& m = *msg;
719 if (!midiNode->queue)
720 midiNode->queue = (void*) new SafeQueue<const struct libremidi::message*>();
721 SafeQueue<const struct libremidi::message*> *que = (SafeQueue<const struct libremidi::message*>*)midiNode->queue;
722 std::cout << "enqueuing one" << std::endl;
723 que->enqueue(new libremidi::message(*msg));
724 std::cout << "enqueued one" << std::endl;
725
726 /*
727 switch (m.get_message_type())
728 {
729 case libremidi::message_type::NOTE_ON:
730 std::cout << "Note ON: "
731 << "channel " << m.get_channel() << ' '
732 << "note " << (int)m.bytes[1] << ' '
733 << "velocity " << (int)m.bytes[2] << ' ';
734 break;
735 case libremidi::message_type::NOTE_OFF:
736 std::cout << "Note OFF: "
737 << "channel " << m.get_channel() << ' '
738 << "note " << (int)m.bytes[1] << ' '
739 << "velocity " << (int)m.bytes[2] << ' ';
740 break;
741 case libremidi::message_type::CONTROL_CHANGE:
742 std::cout << "Control: "
743 << "channel " << m.get_channel() << ' '
744 << "control " << (int)m.bytes[1] << ' '
745 << "value " << (int)m.bytes[2] << ' ';
746 break;
747 }
748 */
749}
750void midiOut_takepacket(MidiNode* midiNode, timedpacket tpacket) {
751 if (!midiNode->queue)
752 midiNode->queue = (void*) new SafeQueue<timedpacket>();
753 SafeQueue<timedpacket>* que = (SafeQueue<timedpacket>*)midiNode->queue;
754 std::cout << "enqueuing one" << std::endl;
755 que->enqueue(tpacket);
756 std::cout << "enqueued one" << std::endl;
757}
758
759void midiProgram_takemessage(MidiNode* mnode, const struct libremidi::message* msg) {
760 struct libremidi::message m = libremidi::message(*msg);
761 if (msg->get_message_type() == libremidi::message_type::PROGRAM_CHANGE) {
762 std::vector<unsigned char> mess(msg->size());
763 mess[0] = msg->bytes[0];
764 mess[1] = mnode->instrument - 1;
765 m = libremidi::message(mess, msg->timestamp);
766 mnode->last_instrument = mnode->instrument;
767 }
768 else if (mnode->instrument != mnode->last_instrument) {
769 // on startup as we get the first note, do a program change first
770 // or if routing to the MIDIProgram.instrument field changed the instrument on the fly
771 // then we send out an extra message before the notes begin
772 ubyte channel, command;
773 channel = (msg->bytes[0] & 0xF);
774 command = command = PROGRAM_CHANGE;
775 std::vector<unsigned char> mess(2);
776 mess[0] = command | channel;
777 mess[1] = mnode->instrument - 1;
778 mnode->last_instrument = mnode->instrument;
779 struct libremidi::message mi = libremidi::message(mess, msg->timestamp);
780 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
781 {
782 MidiNode* mout = *it;
783 if (mout->takemessage) mout->takemessage(mout, &mi);
784 }
785 }
786
787 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
788 {
789 MidiNode* mout = *it;
790 if (mout->takemessage) mout->takemessage(mout, &m);
791 }
792
793}
794void midiProgram_takepacket(MidiNode* mnode, timedpacket tpacket) {
795 ubyte channel, command, note;
796 ushort velocity;
797 midiump_packet2values(tpacket.packet, &channel, &command, &note, &velocity);
798 if (command == PROGRAM_CHANGE) {
799 //intercept and change program
800 UMP* ump = (UMP*)&tpacket.packet;
801 ump->bytes[4] = mnode->instrument -1;
802 mnode->last_instrument = mnode->instrument;
803 }
804 else if (mnode->instrument != mnode->last_instrument) {
805 // on startup as we get the first note, do a program change first
806 // or if routing to the instrument field changed the instrument on the fly
807 // then we send out an extra packet before the notes begin
808 UMP ump;
809 ump.packet = 0.0;
810 command = PROGRAM_CHANGE;
811 ump.bytes[1] = command | (channel-1);
812 ump.bytes[4] = mnode->instrument-1;
813 mnode->last_instrument = mnode->instrument;
814 timedpacket tp;
815 tp.packet = ump.packet;
816 tp.timestamp = tpacket.timestamp;
817 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
818 {
819 MidiNode* mout = *it;
820 //MIDI 2.0 UMP
821 if (mout->takepacket) mout->takepacket(mout, tp);
822 }
823 }
824
825 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
826 {
827 MidiNode* mout = *it;
828 //MIDI 2.0 UMP
829 if (mout->takepacket) mout->takepacket(mout, tpacket);
830 }
831}
832double Time1970sec();
833//MIDIDelay
834void midiDelay_takemessage(MidiNode* mnode, const struct libremidi::message* msg) {
835 struct libremidi::message m = *msg;
836 if (!mnode->queue)
837 mnode->queue = (void*) new SafeQueue<const struct libremidi::message*>();
838 SafeQueue<const struct libremidi::message*>* que = (SafeQueue<const struct libremidi::message*>*)mnode->queue;
839 std::cout << "enqueuing one" << std::endl;
840 m.timestamp = Time1970sec(); //overwrite with absolute time, will change back to diff time below
841 que->enqueue(new libremidi::message(m));
842 std::cout << "enqueued one" << std::endl;
843
844}
845void midiDelay_takepacket(MidiNode* mnode, timedpacket tpacket) {
846 if (!mnode->queue)
847 mnode->queue = (void*) new SafeQueue<timedpacket>();
848 SafeQueue<timedpacket>* que = (SafeQueue<timedpacket>*)mnode->queue;
849 std::cout << "enqueuing one" << std::endl;
850 tpacket.timestamp = Time1970sec(); //swap in absolute time, will replace with delta-time below
851 que->enqueue(tpacket);
852 std::cout << "enqueued one" << std::endl;
853
854}
855
856void mididelayfunctionMSG(MidiNode* mnode) {
857 double now, diff, diff1, diff2;
858 if (!mnode->queue)
859 mnode->queue = (void*) new SafeQueue<const struct libremidi::message*>();
860 SafeQueue<const struct libremidi::message*>* que = (SafeQueue<const struct libremidi::message*>*)mnode->queue;
861
862 //https://cplusplus.com/reference/ctime/time/ //time, difftime, mktime to seconds
863 mnode->starttime = Time1970sec();
864 mnode->lasttime = 0.0; //last outgoing packet time
865 do {
866 while (que->empty()) std::this_thread::sleep_for(std::chrono::milliseconds(50));
867 libremidi::message msg = *(que->dequeue());
868 now = Time1970sec();
869 diff1 = (now - mnode->starttime);
870 diff2 = (msg.timestamp + mnode->delay - mnode->starttime);
871 diff = diff2 - diff1;
872 //printf("diff1 %lf diff2 %lf diff %lf\n", diff1, diff2, diff);
873 if (diff > 0.0)
874 std::this_thread::sleep_for(std::chrono::milliseconds((long long)(diff * 1000.0)));
875 msg.timestamp = Time1970sec() - mnode->lasttime; //packets have the delta-time from last packet sent
876 mnode->lasttime = msg.timestamp;
877 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
878 {
879 MidiNode* mout = *it;
880 //MIDI 2.0 UMP
881 if (mout->takepacket) mout->takemessage(mout, &msg);
882 }
883 } while (TRUE);
884 std::cout << "bye bye" << std::endl;
885
886}
887void mididelayfunctionUMP(MidiNode* mnode) {
888 double now, diff, diff1, diff2;
889 if (!mnode->queue)
890 mnode->queue = (void*) new SafeQueue<timedpacket>();
891 SafeQueue<timedpacket>* que = (SafeQueue<timedpacket>*)mnode->queue;
892
893 //https://cplusplus.com/reference/ctime/time/ //time, difftime, mktime to seconds
894 mnode->starttime = Time1970sec();
895 mnode->lasttime = 0.0; //last outgoing packet time
896 do {
897 while (que->empty()) std::this_thread::sleep_for(std::chrono::milliseconds(50));
898 timedpacket tpacket = que->dequeue();
899 now = Time1970sec();
900 diff1 = (now - mnode->starttime);
901 diff2 = (tpacket.timestamp + mnode->delay - mnode->starttime);
902 diff = diff2 - diff1;
903 //printf("diff1 %lf diff2 %lf diff %lf\n", diff1, diff2, diff);
904 if(diff > 0.0)
905 std::this_thread::sleep_for(std::chrono::milliseconds((long long) ( diff * 1000.0)));
906 tpacket.timestamp = Time1970sec() - mnode->lasttime; //packets have the delta-time from last packet sent
907 mnode->lasttime = tpacket.timestamp;
908 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
909 {
910 MidiNode* mout = *it;
911 //MIDI 2.0 UMP
912 if (mout->takepacket) mout->takepacket(mout, tpacket);
913 }
914 } while (TRUE);
915 std::cout << "bye bye" << std::endl;
916
917}
918void mididelayfunction(MidiNode* mnode) {
919 if (MIDITransport() == MIDI_MSG)
920 mididelayfunctionMSG(mnode);
921 else if (MIDITransport() == MIDI_UMP)
922 mididelayfunctionUMP(mnode);
923}
924
925
926
927void midimsg_uint2values(unsigned int msg, ubyte* channel, ubyte* command, ubyte* note, ubyte* velocity);
928unsigned int midimsg_values2uint(ubyte channel, ubyte command, ubyte note, ubyte velocity);
929void midiump_packet2values(double packet, ubyte* channel, ubyte* command, ubyte* note, ushort* velocity);
930double midiump_values2packet(ubyte channel, ubyte command, ubyte note, ushort velocity);
931
932void mark_event(struct X3D_Node* from, int totalptr);
933//#define MARK_EVENT(node,offset) mark_event(X3D_NODE(node),(int) offset)
934void midiOut_message2fields(MidiNode* midiNode, struct X3D_MIDIOut* node) {
935 // dequeues direct midi messages and converts to MFInt32 outputOnly midiMsg field entries
936 struct X3D_Node* anode = X3D_NODE(node);
937 const struct libremidi::message *msg;
938 if (!midiNode->queue)
939 midiNode->queue = (void*) new SafeQueue<const struct libremidi::message*>();
940 SafeQueue<const struct libremidi::message*>* que = (SafeQueue<const struct libremidi::message*>*)midiNode->queue;
941 Multi_Int32* last = &node->midiMsg;
942 unsigned int cur[1000], n = 0;
943 //int pedal = FALSE;
944 int mark = FALSE;
945 //std::cout << "starting dequeue loop" << std::endl;
946
947 while (!que->empty()) { //msg = que->dequeue()
948 msg = que->dequeue();
949 std::cout << "dequed one" << std::endl;
950
951 const struct libremidi::message& m = *msg;
952 switch (m.get_message_type())
953 {
954 case libremidi::message_type::NOTE_ON:
955 std::cout << "Note ON: "
956 << "channel " << m.get_channel() << ' '
957 << "note " << (int)m.bytes[1] << ' '
958 << "velocity " << (int)m.bytes[2] << ' ';
959 //cur[n] = (int)m.bytes[2] > 0 ? (int)m.bytes[1] : -(int)m.bytes[1];
960 cur[n] = midimsg_values2uint(m.get_channel(), (ubyte)m.get_message_type(), m.bytes[1], m.bytes[2]);
961 n++;
962 break;
963 case libremidi::message_type::NOTE_OFF:
964 std::cout << "Note OFF: "
965 << "channel " << m.get_channel() << ' '
966 << "note " << (int)m.bytes[1] << ' '
967 << "velocity " << (int)m.bytes[2] << ' ';
968 //cur[n] = -(int)m.bytes[1]; //negative sign for OFF, + for ON
969 cur[n] = midimsg_values2uint(m.get_channel(), (ubyte)m.get_message_type(), m.bytes[1], m.bytes[2]);
970 n++;
971 break;
972 case libremidi::message_type::CONTROL_CHANGE:
973 std::cout << "Control: "
974 << "channel " << m.get_channel() << ' '
975 << "control " << (int)m.bytes[1] << ' '
976 << "value " << (int)m.bytes[2] << ' ';
977 //pedal = node->pedal;
978 //node->pedal = m.bytes[2] > 0 ? TRUE : FALSE;
979 //if(pedal != node->pedal) MARK_EVENT(anode, offsetof(struct X3D_MIDIOut, pedal));
980 cur[n] = midimsg_values2uint(m.get_channel(), (ubyte)m.get_message_type(), m.bytes[1], m.bytes[2]);
981 n++;
982 break;
983 default:
984 break;
985 }
986
987 //I don't have a destructor,memory use will escalate ~msg();
988 }
989 //std::cout << "ended dequeue loop" << std::endl;
990
991 if (n) {
992 node->midiMsg.p = (int *)realloc(node->midiMsg.p, n * sizeof(int));
993 memcpy(node->midiMsg.p, cur, n * sizeof(int));
994 node->midiMsg.n = n;
995 MARK_EVENT(anode, offsetof(struct X3D_MIDIOut, midiMsg));
996 }
997 else {
998 node->midiMsg.n = 0;
999 }
1000 //std::cout << "finished midiOut render" << std::endl;
1001
1002}
1003
1004void midiOut_packet2fields(MidiNode* midiNode, struct X3D_MIDIOut* node) {
1005 // dequeues direct midi messages and converts to MFInt32 outputOnly midiMsg field entries
1006 struct X3D_Node* anode = X3D_NODE(node);
1007 if (!midiNode->queue)
1008 midiNode->queue = (void*) new SafeQueue<timedpacket>();
1009 SafeQueue<timedpacket>* que = (SafeQueue<timedpacket>*)midiNode->queue;
1010 Multi_Double* last = &node->midiUmp;
1011 double cur[1000];
1012 int n = 0;
1013 int mark = FALSE;
1014 //std::cout << "starting dequeue loop" << std::endl;
1015 UMP ump;
1016 timedpacket tpacket;
1017 while (!que->empty()) {
1018 tpacket = que->dequeue();
1019 ump.packet = tpacket.packet;
1020 if (1) {
1021 ubyte channel, command, note;
1022 ushort velocity;
1023 midiump_packet2values(ump.packet, &channel, &command, &note, &velocity);
1024 printf("chan %d comm %d note %d vel %d", channel, command, note, velocity);
1025 //std::cout << "chan " << channel << " comm " << command << " note " << note << " vel " << velocity << std::endl;
1026 std::cout << "dequed one in midiOut_packet2fields" << std::endl;
1027 }
1028 cur[n] = ump.packet;
1029 n = n >= 999 ? 999 : n + 1; //we'll drop packets if we get flooded.
1030 }
1031 if (n) {
1032 node->midiUmp.p = (double*)realloc(node->midiUmp.p, n * sizeof(double));
1033 memcpy(node->midiUmp.p, cur, n * sizeof(double));
1034 node->midiUmp.n = n;
1035 MARK_EVENT(anode, offsetof(struct X3D_MIDIOut, midiUmp));
1036 }
1037 else {
1038 node->midiUmp.n = 0;
1039 }
1040 //std::cout << "finished midiOut render" << std::endl;
1041
1042}
1043double TickTime();
1044void midiin_midinote2messages(MidiNode* mnode, struct X3D_MIDIIn* pnode) {
1045 static double lasttime = 0.0;
1046 if (lasttime == 0.0) lasttime = TickTime();
1047 for (int i = 0; i < pnode->midiMsg.n; i++) {
1048 //libremidi::message *msg = new libremidi:message()
1049 ubyte channel, command, note, velocity;
1050 midimsg_uint2values(pnode->midiMsg.p[i], &channel, &command, &note, &velocity);
1051 //unsigned char inote = abs(pnode->midiMsg.p[i]);
1052 //unsigned char velocity = pnode->midiMsg.p[i] > 0 ? 64 : 0;
1053 //std::vector<unsigned char> messout(3);
1054 //messout[0] = 144; // 176; //its a note
1055 //messout[1] = inote;
1056 //messout[2] = on;
1057 double now = TickTime();
1058 double timestamp = now - lasttime;
1059 lasttime = now;
1060 libremidi::message msg; // = libremidi::message(messout, timestamp);
1061 std::vector<unsigned char> messout(3);
1062 messout[0] = command | channel;
1063 messout[1] = note;
1064 messout[2] = velocity;
1065 msg = libremidi::message(messout, timestamp);
1066 printf("MI %d %d %d %lf", messout[0], messout[1], messout[2], timestamp);
1067 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
1068 {
1069 MidiNode* mout = *it;
1070 //std::cout << "mout->takemessage=" << mout->takemessage << std::endl;
1071 if (mout->takemessage) mout->takemessage(mout, &msg);
1072 }
1073
1074 }
1075 pnode->midiMsg.n = 0;
1076}
1077void midiin_midinote2packets(MidiNode* mnode, struct X3D_MIDIIn* pnode) {
1078 static double lasttime = 0.0;
1079 if (lasttime == 0.0) lasttime = TickTime();
1080 for (int i = 0; i < pnode->midiUmp.n; i++) {
1081 double packet = pnode->midiUmp.p[i];
1082 double now = TickTime();
1083 double timestamp = now - lasttime;
1084 lasttime = now;
1085 timedpacket tpacket;
1086 tpacket.packet = packet;
1087 if (1) {
1088 ubyte channel, command, note;
1089 ushort velocity;
1090 midiump_packet2values(packet, &channel, &command, &note, &velocity);
1091 printf("midiin_midinote2packets chan %d comm %d not %d vel %d\n", channel, command, note, velocity);
1092 }
1093 tpacket.timestamp = timestamp;
1094 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
1095 {
1096 MidiNode* mout = *it;
1097 if (mout->takepacket) mout->takepacket(mout, tpacket);
1098 }
1099 }
1100 pnode->midiMsg.n = 0;
1101}
1102
1103
1104static int ports_printed = FALSE;
1105void print_ports() {
1106 //do once per run if there are midi nodes in scene
1107 midiout.close_port();
1108 midiin.close_port();
1109 printf("MIDI Output ports:\n");
1110 std::string portName;
1111 unsigned int i = 0, nPorts = midiout.get_port_count();
1112 if (nPorts == 0)
1113 std::cout << "No ports available!" << std::endl;
1114 else
1115 for (i = 0; i < nPorts; i++)
1116 {
1117 portName = midiout.get_port_name(i);
1118 std::cout << " port #" << i << ": " << portName << '\n';
1119 }
1120 printf("MIDI Input ports:\n");
1121 i = 0, nPorts = midiin.get_port_count();
1122 if (nPorts == 0)
1123 std::cout << "No ports available!" << std::endl;
1124 else
1125 for (i = 0; i < nPorts; i++)
1126 {
1127 portName = midiin.get_port_name(i);
1128 std::cout << " port #" << i << ": " << portName << '\n';
1129 }
1130
1131 ports_printed = TRUE;
1132}
1133MidiNode* midiin_node = NULL;
1134
1135void midiin_C_callback(const libremidi::message* msg){
1136 //called by libremidi when there's port input event
1137 const struct libremidi::message& messin = *msg;
1138
1139 MidiNode* mnode = midiin_node;
1140 auto nBytes = messin.size();
1141 if (MIDITransport() == MIDI_UMP) {
1142 // MIDI 2.0 64 bit UMP packet transport
1143 double packets[100];
1144 const libremidi::midi_bytes bytes[200];
1145 double npacket = msg2ump(nBytes, messin.bytes.data(), packets, TRUE); // messin.timestamp);
1146 if (npacket) {
1147 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
1148 {
1149 MidiNode* mout = *it;
1150 if (mout->takepacket)
1151 for (int i = 0; i < npacket; i++) {
1152 timedpacket tpacket;
1153 tpacket.packet = packets[i];
1154 tpacket.timestamp = messin.timestamp;
1155 mout->takepacket(mout, tpacket);
1156 }
1157 }
1158 }
1159 }
1160 else if(MIDITransport() == MIDI_MSG) {
1161 //MIDI 1 byte stream message transport
1162 std::vector<unsigned char> messout(messin.size());
1163
1164 std::cout << "CB ";
1165 for (auto i = 0U; i < nBytes; i++)
1166 std::cout << "Byte " << i << " = " << (int)messin[i] << ", ";
1167 if (nBytes > 0)
1168 std::cout << "stamp = " << messin.timestamp << std::endl;
1169 messout[0] = messin[0];
1170 messout[1] = messin[1];
1171 messout[2] = messin[2];
1172 if (messout[2] > 0 && messout[2] < 64)
1173 messout[2] = 64;
1174 libremidi::message msgo = libremidi::message(messout, messin.timestamp);
1175 //midiout.send_message(messout);
1176 for (std::list<MidiNode*>::iterator it = mnode->outputs.begin(); it != mnode->outputs.end(); ++it)
1177 {
1178 MidiNode* mout = *it;
1179 //std::cout << "mout->takemessage=" << mout->takemessage << std::endl;
1180 if (mout->takemessage) mout->takemessage(mout, &msgo);
1181 }
1182 }
1183}
1184//std::function<void(libremidi::message*)> standard_function(midiin_C_callback);
1185//libremidi::midi_in::message_callback standard_function(midiin_C_callback);
1186void libmidi_updateNode3(int icontext, icset connect_parent, struct X3D_Node* node) {
1187 // midi node type 1=PortSource 2=PortDestination 3=FileSource 4=FileDestination 5=PrintDestination 6=MIDIOut
1188 struct mcstruct* ac = midi_contexts[icontext];
1189 //goal- switch-case on x3d nodeType and do any midinode create+connect, update input or update output
1190 struct X3D_MidiRep* srepn = (struct X3D_MidiRep*)node->_intern;
1191 if (!ports_printed) print_ports();
1192 switch (node->_nodeType) {
1193 case NODE_MIDIPortSource:
1194 {
1195 struct X3D_MIDIPortSource* pnode = (struct X3D_MIDIPortSource*)node;
1196 MidiNode* input;
1197 if (!srepn->inode) {
1198 input = new MidiNode();
1199 input->itype = 1; //1=MIDIPortSource
1200 input->numberOfOutputs = 0;
1201 input->numberOfInputs = 1;
1202 input->takemessage = NULL;
1203 midiin_node = input;
1204 midiin.open_port(pnode->port, "libremidi Input");
1205 //chooseMidiPortIn(midiin);
1206 printf("opened source port %d\n", pnode->port);
1207 printf("have a MIDIPortSource node, handling it'n");
1208 set_midiin_callback();
1209 //..midiin.set_callback((void*)midiin_C_callback); // standard_function); // midiin_callback);
1210 /*
1211 midiin.set_callback(
1212 [](const libremidi::message& message)
1213 {
1214 std::vector<unsigned char> messout(message.size());
1215 auto nBytes = message.size();
1216 for (auto i = 0U; i < nBytes; i++)
1217 std::cout << "Byte " << i << " = " << (int)message[i] << ", ";
1218 if (nBytes > 0)
1219 std::cout << "stamp = " << message.timestamp << std::endl;
1220 messout[0] = message[0];
1221 messout[1] = message[1];
1222 messout[2] = message[2];
1223 if (messout[2] > 0 && messout[2] < 64)
1224 messout[2] = 64;
1225 midiout.send_message(messout);
1226
1227 });
1228 */
1229 // Don't ignore sysex, timing, or active sensing messages.
1230 midiin.ignore_types(false, false, false);
1231
1232 ac->next_node++;
1233 ac->nodes[ac->next_node] = input;
1234 ac->nodetype[ac->next_node] = NODE_MIDIPortSource;
1235 srepn->inode = ac->next_node;
1236 srepn->icontext = icontext;
1237 }
1238
1239 }
1240 break;
1241 case NODE_MIDIPortDestination:
1242 {
1243 struct X3D_MIDIPortDestination* pnode = (struct X3D_MIDIPortDestination*)node;
1244 MidiNode* input;
1245 if (!srepn->inode) {
1246 input = new MidiNode();
1247 input->itype = 2; //MIDIPortDestination
1248 input->numberOfOutputs = 0;
1249 input->numberOfInputs = 1;
1250 input->takemessage = midiPortDestination_takemessage;
1251 input->takepacket = midiPortDestination_takepacket;
1252 midiout.open_port(pnode->port, "libremidi Output");
1253 printf("opened destination port %d\n", pnode->port);
1254 //std::thread portsource(midiportsourcefunction, input);
1255 //portsource.detach(); //so it doesn't try and join when done
1256
1257 ac->next_node++;
1258 ac->nodes[ac->next_node] = input;
1259 ac->nodetype[ac->next_node] = NODE_MIDIPortDestination;
1260 srepn->inode = ac->next_node;
1261 srepn->icontext = icontext;
1262 }
1263
1264 }
1265 break;
1266
1267 case NODE_MIDIFileSource:
1268 {
1269 struct X3D_MIDIFileSource* pnode = (struct X3D_MIDIFileSource*)node;
1270 MidiNode* input;
1271 if (!srepn->inode) {
1272 input = new MidiNode();
1273 input->itype = 3; //MIDIFileSource
1274 input->numberOfOutputs = 1;
1275 input->numberOfInputs = 0;
1276 input->takemessage = NULL;
1277 input->loop = FALSE;
1278 input->run = FALSE;
1279 // Initialize our reader object
1280 libremidi::reader *midireader = new libremidi::reader(true); //use abolute? time I think
1281
1282 // Parse
1283 libremidi::reader::parse_result result = midireader->parse((uint8_t*)pnode->__blob.p,pnode->__blob.n);
1284 printf("updateNod3 for FileSource");
1285 // If parsing succeeded, use the parsed data
1286 if (result != libremidi::reader::invalid) {
1287 input->reader = midireader;
1288 printf("starting filesource thread\n");
1289 std::thread filesource(midifilesourcefunction, input);
1290 filesource.detach(); //so it doesn't try and join when done
1291 //for (auto& track : r.tracks) {
1292 // for (const libremidi::track_event& event : track) {
1293 // std::cout << (int)event.m.bytes[0] << '\n';
1294 // }
1295 //}
1296 }
1297
1298 ac->next_node++;
1299 ac->nodes[ac->next_node] = input;
1300 ac->nodetype[ac->next_node] = NODE_MIDIFileSource;
1301 srepn->inode = ac->next_node;
1302 srepn->icontext = icontext;
1303 //if (iparent.x)
1304 // libsound_connect2(icontext, iparent.x, srepn->inode, iparent.y, iparent.z);
1305 }
1306 else {
1307 input = ac->nodes[srepn->inode];
1308 //printf("setting source node to run\n");
1309 input->run = TRUE;
1310 //copy changed values from x3d to libmidi
1311 }
1312 }
1313 break;
1314 case NODE_MIDIPrintDestination:
1315 {
1316 struct X3D_MIDIPrintDestination* pnode = (struct X3D_MIDIPrintDestination*)node;
1317 MidiNode* input;
1318 if (!srepn->inode) {
1319 input = new MidiNode();
1320 input->itype = 5; //MIDIPrintDestination
1321 input->numberOfOutputs = 0;
1322 input->numberOfInputs = 1;
1323 input->takemessage = midiPrintDestination_takemessage;
1324 input->takepacket = midiPrintDestination_takepacket;
1325 ac->next_node++;
1326 ac->nodes[ac->next_node] = input;
1327 ac->nodetype[ac->next_node] = NODE_MIDIPrintDestination;
1328 srepn->inode = ac->next_node;
1329 srepn->icontext = icontext;
1330 }
1331
1332 }
1333 break;
1334 case NODE_MIDIOut:
1335 {
1336 struct X3D_MIDIOut* pnode = (struct X3D_MIDIOut*)node;
1337 MidiNode* input;
1338 if (!srepn->inode) {
1339 input = new MidiNode();
1340 input->itype = 6;
1341 input->numberOfOutputs = 0;
1342 input->numberOfInputs = 1;
1343 input->takemessage = midiOut_takemessage;
1344 input->takepacket = midiOut_takepacket;
1345
1346 ac->next_node++;
1347 ac->nodes[ac->next_node] = input;
1348 ac->nodetype[ac->next_node] = NODE_MIDIOut;
1349 srepn->inode = ac->next_node;
1350 srepn->icontext = icontext;
1351 }
1352 //update x3d node fields
1353 input = ac->nodes[srepn->inode];
1354 //read input queue and convert to MFInt32 midiNote and SFInt32 pedal
1355 if(MIDITransport() == MIDI_MSG)
1356 midiOut_message2fields(input, pnode);
1357 if (MIDITransport() == MIDI_UMP)
1358 midiOut_packet2fields(input, pnode);
1359 }
1360 break;
1361 case NODE_MIDIIn:
1362 {
1363 struct X3D_MIDIIn* pnode = (struct X3D_MIDIIn*)node;
1364 MidiNode* input;
1365 if (!srepn->inode) {
1366 input = new MidiNode();
1367 input->itype = 7;
1368 input->numberOfOutputs = 1;
1369 input->numberOfInputs = 0;
1370 input->takemessage = NULL; //it takes a normal ROUTE, not midi messages
1371 input->takepacket = NULL;
1372
1373 ac->next_node++;
1374 ac->nodes[ac->next_node] = input;
1375 ac->nodetype[ac->next_node] = NODE_MIDIIn;
1376 srepn->inode = ac->next_node;
1377 srepn->icontext = icontext;
1378 }
1379 input = ac->nodes[srepn->inode];
1380 //convert MFInt32 midiNote to string of messages with this timestamp
1381 if(MIDITransport() == MIDI_MSG)
1382 midiin_midinote2messages(input, pnode);
1383 if (MIDITransport() == MIDI_UMP)
1384 midiin_midinote2packets(input, pnode);
1385
1386 }
1387 break;
1388 case NODE_MIDIProgram:
1389 {
1390 struct X3D_MIDIProgram* pnode = (struct X3D_MIDIProgram*)node;
1391 MidiNode* input;
1392 if (!srepn->inode) {
1393 input = new MidiNode();
1394 input->itype = 8;
1395 input->numberOfOutputs = 1;
1396 input->numberOfInputs = 1;
1397 input->takemessage = midiProgram_takemessage; //it takes a normal ROUTE, not midi messages
1398 input->takepacket = midiProgram_takepacket;
1399 input->last_instrument = 0;
1400
1401 ac->next_node++;
1402 ac->nodes[ac->next_node] = input;
1403 ac->nodetype[ac->next_node] = NODE_MIDIProgram;
1404 srepn->inode = ac->next_node;
1405 srepn->icontext = icontext;
1406 }
1407 input = ac->nodes[srepn->inode];
1408 input->instrument = pnode->instrument;
1409 input = ac->nodes[srepn->inode];
1410
1411 }
1412 break;
1413 case NODE_MIDIDelay:
1414 {
1415 struct X3D_MIDIDelay* pnode = (struct X3D_MIDIDelay*)node;
1416 MidiNode* input;
1417 if (!srepn->inode) {
1418 input = new MidiNode();
1419 input->itype = 8;
1420 input->numberOfOutputs = 1;
1421 input->numberOfInputs = 1;
1422 input->takemessage = midiDelay_takemessage; //it takes a normal ROUTE, not midi messages
1423 input->takepacket = midiDelay_takepacket;
1424 input->delay = 0;
1425 std::thread mididelay(mididelayfunction, input);
1426 mididelay.detach(); //so it doesn't try and join when done
1427
1428 ac->next_node++;
1429 ac->nodes[ac->next_node] = input;
1430 ac->nodetype[ac->next_node] = NODE_MIDIDelay;
1431 srepn->inode = ac->next_node;
1432 srepn->icontext = icontext;
1433 }
1434 input = ac->nodes[srepn->inode];
1435 input->delay = pnode->delay;
1436 input = ac->nodes[srepn->inode];
1437
1438 }
1439 break;
1440
1441
1442 default:
1443 break;
1444 }
1445}
1446void libmidi_pauseContext0(int icontext) {}
1447void libmidi_resumeContext0(int icontext) {}
1448
1449
1450static struct type_name {
1451 int iname;
1452 const char* cname;
1453} type_names[] = {
1454{NODE_MIDIPortDestination, "MPD"},
1455{NODE_MIDIPortSource, "MPS"},
1456{NODE_MIDIFileDestination, "MFD"},
1457{NODE_MIDIFileSource, "MFS"},
1458{NODE_MIDIIn, "MIn"},
1459{NODE_MIDIOut, "MOut"},
1460{NODE_MIDIProgram,"PRG"},
1461{NODE_MIDIDelay,"DLY"},
1462{0,NULL},
1463};
1464static const char* nodetype_lookup(int itype) {
1465 int i;
1466 const char* cname;
1467 struct type_name* tn;
1468 i = 0;
1469 cname = "";
1470 do {
1471 tn = &type_names[i];
1472 if (tn->iname == itype) {
1473 cname = tn->cname;
1474 break;
1475 }
1476 i++;
1477 } while (tn->iname != 0);
1478 return cname;
1479
1480}
1481//this one uses int index lookup in a map, much like a vector except can deleted elements
1482struct midiconnection {
1483 int icontext;
1484 int iparent;
1485 int iparent_type;
1486 int ichild;
1487 int ichild_type;
1488 int srcindex;
1489 int dstindex;
1490};
1491static std::list<midiconnection> midiconnections;
1492void context_disconnect(MidiNode* destination, MidiNode* source, int indexDst, int indexSrc) {
1493 source->outputs.remove(destination);
1494 //destination->inputs.remove(source);
1495}
1496void context_connect(MidiNode* destination, MidiNode* source, int indexDst, int indexSrc) {
1497 source->outputs.push_back(destination);
1498 //destination->inputs.push_back(source);
1499}
1500void libmidi_connect2(int icontext, int idestination, int isource, int indexDst, int indexSrc) {
1501 struct mcstruct* ac = midi_contexts[icontext];
1502 MidiNode* destination = ac->nodes[idestination];
1503 MidiNode* source = ac->nodes[isource];
1504 int dstInputs = destination->numberOfInputs;
1505 int srcOutputs = source->numberOfOutputs;
1506 if (indexDst > dstInputs) {
1507 printf("destination number of inputs %d destination idx %d\n", destination->numberOfInputs, indexDst);
1508 printf("\n");
1509 printf("\n");
1510 printf("\n");
1511 printf("\n");
1512 return;
1513 }
1514 if (indexSrc > srcOutputs) {
1515 printf("source number of outputs %d source idx %d\n", srcOutputs, indexSrc);
1516 printf("\n");
1517 printf("\n");
1518 printf("\n");
1519 printf("\n");
1520 return;
1521 }
1522
1523 //ac->context->connect(destination, source, indexDst, indexSrc);
1524 context_connect(destination, source, indexDst, indexSrc);
1525 int iparent_type = ac->nodetype[idestination];
1526 int ichild_type = ac->nodetype[isource];
1527 struct midiconnection cc; cc.icontext = icontext; cc.iparent = idestination; cc.iparent_type = iparent_type;
1528 cc.ichild = isource; cc.ichild_type = ichild_type; cc.srcindex = indexSrc; cc.dstindex = indexDst;
1529 midiconnections.push_back(cc);
1530
1531}
1532void libmidi_disconnect2(int icontext, int idestination, int isource, int indexDst, int indexSrc) {
1533 struct mcstruct* ac = midi_contexts[icontext];
1534 MidiNode* destination = ac->nodes[idestination];
1535 MidiNode* source = ac->nodes[isource];
1536 int dstInputs = destination->numberOfInputs;
1537 int srcOutputs = source->numberOfOutputs;
1538 if (indexDst > dstInputs) {
1539 printf("destination number of inputs %d destination idx %d\n", destination->numberOfInputs, indexDst);
1540 printf("\n");
1541 printf("\n");
1542 printf("\n");
1543 printf("\n");
1544 return;
1545 }
1546 if (indexSrc > srcOutputs) {
1547 printf("source number of outputs %d source idx %d\n", srcOutputs, indexSrc);
1548 printf("\n");
1549 printf("\n");
1550 printf("\n");
1551 printf("\n");
1552 return;
1553 }
1554
1555 //ac->context->disconnect(destination, source, indexDst, indexSrc);
1556 context_disconnect(destination, source, indexDst, indexSrc);
1557 //find and remove from vector
1558 // vec.erase(vec.begin() + index);
1559 int iparent_type = ac->nodetype[idestination];
1560 int ichild_type = ac->nodetype[isource];
1561 struct midiconnection cc; cc.icontext = icontext; cc.iparent = idestination; cc.iparent_type = iparent_type;
1562 cc.ichild = isource; cc.ichild_type = ichild_type; cc.srcindex = indexSrc; cc.dstindex = indexDst;
1563
1564 std::list<midiconnection>::iterator it;
1565 for (it = midiconnections.begin(); it != midiconnections.end(); ++it) {
1566 midiconnection cn = *it;
1567 if (cn.icontext = cc.icontext && cn.iparent == cc.iparent && cn.ichild == cc.ichild
1568 && cn.srcindex == cc.srcindex && cn.dstindex == cc.dstindex) {
1569 midiconnections.erase(it);
1570 break;
1571 }
1572 }
1573
1574}
1575
1576void libmidi_connect(int icontext, icset iparent) {
1577 if (iparent.p)
1578 libmidi_connect2(icontext, iparent.p, iparent.n, iparent.d, iparent.s);
1579}
1580void libmidi_disconnect(int icontext, icset iparent) {
1581 libmidi_disconnect2(icontext, iparent.p, iparent.n, iparent.ld, iparent.ls);
1582
1583}
1584void libmidi_print_connections() {
1585 printf("\n");
1586 printf("%2s %7s %4s %7s %7s %6s %4s\n", "ic", "iparent", "type", "dstIndx", "srcIndex", "ichild", "type");
1587 //for (int i = 0; i < connections.size(); i++) {
1588 // struct connection cc = connections[i];
1589 std::list<midiconnection>::iterator it;
1590 for (it = midiconnections.begin(); it != midiconnections.end(); ++it) {
1591 midiconnection cc = *it;
1592
1593 const char* ptype = nodetype_lookup(cc.iparent_type);
1594 const char* ctype = nodetype_lookup(cc.ichild_type);
1595
1596 printf("%2d %7d %4s %7d %7d %6d %4s\n", cc.icontext, cc.iparent, ptype, cc.dstindex, cc.srcindex, cc.ichild, ctype);
1597 }
1598 printf("count %d\n", (int)midiconnections.size());
1599}
1600
1601
1602#ifdef __cplusplus
1603}
1604#endif
1605
1606#else // HAVE_LIBREMIDI
1607#endif //HAVE_LIBREMIDI
Definition libmidi.h:18