1 /*
2 * Copyleft (C) 2000 David Griffiths <dave@pawfal.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "Midi.h"
20 #include "unistd.h"
21 #include "sys/types.h"
22 #include "signal.h"
23 #include "pthread.h"
24
25 static const int MIDI_SCANBUFSIZE=256;
26 static const int MIDI_KEYOFFSET=0;
27
28 static const unsigned char STATUS_START = 0x80;
29 static const unsigned char STATUS_NOTE_OFF = 0x80;
30 static const unsigned char STATUS_NOTE_ON = 0x90;
31 static const unsigned char STATUS_AFTERTOUCH = 0xa0;
32 static const unsigned char STATUS_CONTROL_CHANGE = 0xb0;
33 static const unsigned char STATUS_PROG_CHANGE = 0xc0;
34 static const unsigned char STATUS_CHANNEL_PRESSURE = 0xd0;
35 static const unsigned char STATUS_PITCH_WHEEL = 0xe0;
36 static const unsigned char STATUS_END = 0xf0;
37 static const unsigned char SYSEX_START = 0xf0;
38 static const unsigned char SYSEX_TERMINATOR = 0xf7;
39 static const unsigned char MIDI_CLOCK = 0xf8;
40 static const unsigned char ACTIVE_SENSE = 0xfe;
41
42 static int NKEYS = 30;
43
44 MidiDevice *MidiDevice::m_Singleton;
45 string MidiDevice::m_AppName;
46
47 #if __APPLE__
48 #define read AppleRead
49 #endif
50
51 #ifdef USE_OSS_MIDI
52 string MidiDevice::m_DeviceName;
53 #endif
54
Init(const string & name,Type t)55 void MidiDevice::Init(const string &name, Type t)
56 {
57 if (!m_Singleton)
58 {
59 m_AppName=name;
60 m_Singleton=new MidiDevice(t);
61 }
62 }
63
MidiDevice(Type t)64 MidiDevice::MidiDevice(Type t) :
65 m_Poly (1),
66 m_Clock (1.0f),
67 m_ClockCount (0)
68 {
69 #if __APPLE__
70 AppleOpen();
71 #endif
72 #ifdef USE_ALSA_MIDI
73 AlsaOpen();
74 #endif
75 #ifdef USE_OSS_MIDI
76 if (!OssOpen()) return;
77 #endif
78 m_Mutex = new pthread_mutex_t;
79 pthread_mutex_init (m_Mutex, NULL);
80 pthread_create (&m_MidiReader, NULL, (void*(*)(void*))MidiDevice::MidiReaderCallback, (void*)this);
81 }
82
~MidiDevice()83 MidiDevice::~MidiDevice() {
84 pthread_mutex_lock (m_Mutex);
85 pthread_cancel (m_MidiReader);
86 pthread_mutex_unlock (m_Mutex);
87 pthread_mutex_destroy (m_Mutex);
88 #if __APPLE__
89 AppleClose();
90 #endif
91 #ifdef USE_ALSA_MIDI
92 AlsaClose();
93 #endif
94 #ifdef USE_OSS_MIDI
95 OssClose();
96 #endif
97 }
98
99 // returns the next event off the list, or an
100 // empty event if the list is exhausted
GetEvent(int Device)101 MidiEvent MidiDevice::GetEvent(int Device)
102 {
103 if (Device<0 || Device>15)
104 {
105 cerr<<"GetEvent: Invalid Midi device "<<Device<<endl;
106 return MidiEvent(MidiEvent::NONE,0,0);
107 }
108
109 pthread_mutex_lock(m_Mutex);
110 if (m_EventVec[Device].size()==0)
111 {
112 pthread_mutex_unlock(m_Mutex);
113 return MidiEvent(MidiEvent::NONE,0,0);
114 }
115
116 MidiEvent event(m_EventVec[Device].front());
117 m_EventVec[Device].pop();
118 pthread_mutex_unlock(m_Mutex);
119
120 return event;
121 }
122
SendEvent(int Device,const MidiEvent & Event)123 void MidiDevice::SendEvent (int Device, const MidiEvent &Event) {
124 #ifdef USE_ALSA_MIDI
125 AlsaSendEvent (Device, Event);
126 #else
127 if (Device<0 || Device>15)
128 {
129 cerr<<"SendEvent: Invalid Midi device "<<Device<<endl;
130 }
131
132 char message[3];
133
134 message[1]=Event.GetNote()+MIDI_KEYOFFSET;
135 message[2]=(char)Event.GetVolume();
136
137 if (Event.GetType()==MidiEvent::ON)
138 {
139 message[0]=STATUS_NOTE_ON+Device;
140 write(m_MidiWrFd,message,3);
141 //cerr<<"sending "<<message<<endl;
142 }
143
144 if (Event.GetType()==MidiEvent::OFF)
145 {
146 message[0]=STATUS_NOTE_OFF+Device;
147 write(m_MidiWrFd,message,3);
148 //cerr<<"sending "<<message<<endl;
149 }
150 #endif
151 }
152
153 //////////////////////////////////////////// Oss Code Only ////////////////////////////////////////
154
155 #ifdef USE_OSS_MIDI
156
OssOpen()157 bool MidiDevice::OssOpen() {
158 //if (!SpiralInfo::WANTMIDI) return;
159 m_MidiFd = open (m_DeviceName.c_str(), O_RDONLY | O_FSYNC);
160 if (!m_MidiFd) {
161 cerr << "Couldn't open midi for reading [" << m_DeviceName << "]" << endl;
162 return false;
163 }
164 m_MidiWrFd = open (m_DeviceName.c_str(), O_WRONLY);
165 if (!m_MidiWrFd) {
166 cerr << "Couldn't open midi for writing [" << m_DeviceName << "]" << endl;
167 return false;
168 }
169 cerr << "Opened midi device [" << m_DeviceName << "]" << endl;
170 return true;
171 }
172
OssClose()173 void MidiDevice::OssClose() {
174 close(m_MidiFd);
175 close(m_MidiWrFd);
176 cerr<<"Closed midi device"<<endl;
177 }
178
179 // little helper to strip out the realtime and unused messages
OssReadByte(unsigned char * c)180 void MidiDevice::OssReadByte(unsigned char *c)
181 {
182 *c=ACTIVE_SENSE;
183 do read(m_MidiFd,c,1);
184 while (*c>=STATUS_END && *c!=MIDI_CLOCK);
185 }
186
187 // collect events deals with the byte level messages, and sorts
188 // and filters them into distinct messages we can handle easily
189
OssCollectEvents()190 void MidiDevice::OssCollectEvents()
191 {
192 unsigned char buf[1];
193 int count,n,nn;
194 bool MessageSent;
195 unsigned char data[3],last=0;
196
197 // constantly scan for relevent input,
198 // and write it to the pipe
199
200 // filters out unhandled messages, and attempts to build
201 // coherent messages to send to the midi handler
202 bool InSysex=false;
203
204 for(;;)
205 {
206 OssReadByte(buf);
207
208 if (*buf==MIDI_CLOCK)
209 {
210 m_ClockCount++;
211 if (m_ClockCount==6)
212 {
213 m_Clock=-m_Clock;
214 m_ClockCount=0;
215 }
216 }
217 else
218 if (*buf>=STATUS_START) // we've got a status byte
219 {
220 if (*buf==SYSEX_TERMINATOR) InSysex=false;
221
222 // find out if it's an opcode
223 if(*buf>=STATUS_START && *buf<=STATUS_END)
224 {
225 InSysex=false;
226 last=data[0]=*buf;
227
228 if (data[0]>=STATUS_PROG_CHANGE && data[0]<STATUS_PITCH_WHEEL)
229 {
230 OssReadByte(&data[1]); //one byte
231 data[2]=0;
232 }
233 else // get the next two bytes
234 {
235 OssReadByte(&data[1]);
236 OssReadByte(&data[2]);
237 }
238 OssAddEvent(data);
239 }
240 else // its a sysex or other message like active sense
241 {
242 if (*buf==SYSEX_START) InSysex=true;
243 cerr<<"Unhandled midi message: "; printf("%x\n",(int)*buf);
244 }
245 }
246 else // more data (running status)
247 {
248 if (!InSysex)
249 {
250 data[0]=last;
251 data[1]=*buf;
252
253 if (data[0]>=STATUS_PROG_CHANGE && data[0]<STATUS_PITCH_WHEEL)
254 {
255 data[2]=0; //one byte
256 }
257 else // get the next byte
258 {
259 OssReadByte(&data[2]);
260 }
261
262 OssAddEvent(data);
263 }
264 }
265 }
266 }
267
268 // addevent converts the midi bytecode into midi message objects and
269 // stacks them onto the event list to be picked up by the app
OssAddEvent(unsigned char * midi)270 void MidiDevice::OssAddEvent(unsigned char* midi)
271 {
272 MidiEvent::type MessageType=MidiEvent::NONE;
273 int Volume=0,Note=0,EventDevice=0;
274
275 // note off
276 if (midi[0] >= STATUS_NOTE_OFF && midi[0] < STATUS_NOTE_ON)
277 {
278 MessageType=MidiEvent::OFF;
279 Note=midi[1]-MIDI_KEYOFFSET;
280 EventDevice=midi[0]-STATUS_NOTE_OFF;
281 }
282 // note on
283 else if (midi[0] >= STATUS_NOTE_ON && midi[0] < STATUS_AFTERTOUCH)
284 {
285 Volume = midi[2];
286
287 // cope with Roland equipment, where note on's
288 // with zero velocity are sent as note offs.
289 if (Volume) MessageType=MidiEvent::ON;
290 else MessageType=MidiEvent::OFF;
291
292 Note=midi[1]-MIDI_KEYOFFSET;
293 EventDevice=midi[0]-STATUS_NOTE_ON;
294 }
295 // aftertouch
296 else if (midi[0] >= STATUS_AFTERTOUCH && midi[0] < STATUS_CONTROL_CHANGE)
297 {
298 MessageType=MidiEvent::AFTERTOUCH;
299 Note=midi[1]-MIDI_KEYOFFSET;
300 Volume=midi[2];
301 EventDevice=midi[0]-STATUS_AFTERTOUCH;
302 }
303 // parameter
304 else if (midi[0] >= STATUS_CONTROL_CHANGE && midi[0] < STATUS_PROG_CHANGE)
305 {
306 MessageType=MidiEvent::PARAMETER;
307 Note=midi[1];
308 Volume=midi[2];
309 EventDevice=midi[0]-STATUS_CONTROL_CHANGE;
310 }
311 // channel pressure
312 else if (midi[0] >= STATUS_CHANNEL_PRESSURE && midi[0] < STATUS_PITCH_WHEEL)
313 {
314 MessageType=MidiEvent::CHANNELPRESSURE;
315 Volume=midi[1];
316 EventDevice=midi[0]-STATUS_CHANNEL_PRESSURE;
317 }
318 // note pitchbend
319 else if (midi[0] >= STATUS_PITCH_WHEEL && midi[0] < STATUS_END)
320 {
321 MessageType=MidiEvent::PITCHBEND;
322 // should probably take the first byte into account too?
323 Volume=midi[2];
324 EventDevice=midi[0]-STATUS_PITCH_WHEEL;
325 }
326
327 if (EventDevice<0 || EventDevice>15)
328 {
329 cerr<<"Error - Midi device "<<EventDevice<<" ??"<<endl;
330 return;
331 }
332
333 pthread_mutex_lock(m_Mutex);
334 m_EventVec[EventDevice].push(MidiEvent(MessageType,Note,Volume));
335 pthread_mutex_unlock(m_Mutex);
336 }
337
338 #endif
339
340 //////////////////////////////////////////// Alsa Code Only ////////////////////////////////////////
341
342 #ifdef USE_ALSA_MIDI
343
344 // code taken and modified from jack_miniFMsynth
345
AlsaClose()346 void MidiDevice::AlsaClose () {
347
348 //Alsa requires two handles - one for read and one for write,
349 //so we make sure too close both here
350
351 snd_seq_close (seq_rhandle);
352 snd_seq_close (seq_whandle);
353 }
354
AlsaCollectEvents()355 void MidiDevice::AlsaCollectEvents () {
356 //As Alsa only supports a read or write, we use the read handle here to poll our input
357 //for MIDI events
358
359 int seq_nfds, l1;
360 struct pollfd *pfds;
361
362 //get descriptors count to find out how many events are
363 //waiting to be processed
364 seq_nfds = snd_seq_poll_descriptors_count(seq_rhandle, POLLIN);
365
366 //poll the descriptors to be proccessed and loop through them
367 pfds = new struct pollfd[seq_nfds];
368 snd_seq_poll_descriptors(seq_rhandle, pfds, seq_nfds, POLLIN);
369 for (;;) {
370 if (poll (pfds, seq_nfds, 1000) > 0) {
371 for (l1 = 0; l1 < seq_nfds; l1++) {
372 if (pfds[l1].revents > 0) {
373 snd_seq_event_t *ev;
374 // this line looks suspect to me (Andy Preston)
375 // int l1;
376 MidiEvent::type MessageType=MidiEvent::NONE;
377 int Volume=0, Note=0, EventDevice=0;
378 do {
379 snd_seq_event_input (seq_rhandle, &ev);
380 if ((ev->type == SND_SEQ_EVENT_NOTEON) && (ev->data.note.velocity == 0)) {
381 ev->type = SND_SEQ_EVENT_NOTEOFF;
382 }
383 switch (ev->type) {
384 case SND_SEQ_EVENT_PITCHBEND:
385 // Andy Preston
386 MessageType=MidiEvent::PITCHBEND;
387 Volume = (char)((ev->data.control.value / 8192.0)*256);
388 break;
389 case SND_SEQ_EVENT_CONTROLLER:
390 MessageType=MidiEvent::PARAMETER;
391 Note = ev->data.control.param;
392 Volume = ev->data.control.value;
393 break;
394 case SND_SEQ_EVENT_NOTEON:
395 MessageType=MidiEvent::ON;
396 EventDevice = ev->data.control.channel;
397 Note = ev->data.note.note;
398 Volume = ev->data.note.velocity;
399 break;
400 case SND_SEQ_EVENT_NOTEOFF:
401 MessageType=MidiEvent::ON;
402 EventDevice = ev->data.control.channel;
403 Note = ev->data.note.note;
404 break;
405 }
406 pthread_mutex_lock (m_Mutex);
407 m_EventVec[EventDevice].push (MidiEvent (MessageType, Note, Volume));
408 pthread_mutex_unlock (m_Mutex);
409 snd_seq_free_event (ev);
410 } while (snd_seq_event_input_pending(seq_rhandle, 0) > 0);
411 }
412 }
413 }
414 }
415 delete [] pfds;
416 }
417
AlsaSendEvent(int Device,const MidiEvent & Event)418 void MidiDevice::AlsaSendEvent (int Device, const MidiEvent &Event) {
419 //As Alsa only supports a read or write, we use the write handle here to send
420 //our MIDI events
421
422 snd_seq_event_t ev;
423
424 snd_seq_ev_clear (&ev);
425 snd_seq_ev_set_direct (&ev);
426 snd_seq_ev_set_subs (&ev);
427 snd_seq_ev_set_source (&ev, 0);
428
429 switch (Event.GetType())
430 {
431 case MidiEvent::ON:
432 ev.type = SND_SEQ_EVENT_NOTEON;
433 break;
434 case MidiEvent::OFF:
435 ev.type = SND_SEQ_EVENT_NOTEOFF;
436 break;
437 /* case MidiEvent::PARAMETER:
438 ev.type = SND_SEQ_EVENT_CONTROLLER;
439 ev.data.control.param = Event.GetNote();
440 ev.data.control.value =
441 break;
442 case MidiEvent::PITCHBEND:
443 ev.type = SND_SEQ_EVENT_PITCHBEND;
444 ev.data.control.param = Event.GetNote();
445 ev.data.control.value =
446 break;*/
447 default:
448 break;
449 }
450
451 ev.data.note.velocity = (char)Event.GetVolume()*127;
452 ev.data.control.channel = Device;
453 ev.data.note.note=Event.GetNote();
454
455 snd_seq_event_output(seq_whandle, &ev);
456 snd_seq_drain_output(seq_whandle);
457 }
458
AlsaOpen()459 void MidiDevice::AlsaOpen()
460 {
461 int client_id, port_id;
462
463 //Alsa apears to require two handles, one for read and one for write
464 //so we try to open one first for input and then one for output
465
466 //open input handle
467 if (snd_seq_open(&seq_rhandle, "default", SND_SEQ_OPEN_INPUT, 0) < 0)
468 {
469 fprintf(stderr, "Error opening ALSA input sequencer.\n");
470 exit(1);
471 }
472
473 //setup our input name as seen by other apps, and get corresponding client id
474 snd_seq_set_client_name(seq_rhandle, m_AppName.c_str());
475 client_id = snd_seq_client_id(seq_rhandle);
476
477 //try and create our actual input port capable of being written to by MIDI outputs
478 if ((port_id = snd_seq_create_simple_port(seq_rhandle, m_AppName.c_str(),
479 SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE,
480 SND_SEQ_PORT_TYPE_APPLICATION) < 0))
481 {
482 fprintf(stderr, "Error creating input sequencer port.\n");
483 }
484
485 //open output handle
486 if (snd_seq_open(&seq_whandle, "default", SND_SEQ_OPEN_OUTPUT, 0) < 0)
487 {
488 fprintf(stderr, "Error opening ALSA ouput sequencer.\n");
489 exit(1);
490 }
491
492 //setup our output name as seen by other apps, and get corresponding client id
493 snd_seq_set_client_name(seq_whandle, m_AppName.c_str());
494 client_id = snd_seq_client_id(seq_whandle);
495
496 //try and create our actual output port capable of being read from by MIDI inputs
497 if ((port_id = snd_seq_create_simple_port(seq_whandle, m_AppName.c_str(),
498 SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ,
499 SND_SEQ_PORT_TYPE_APPLICATION) < 0))
500 {
501 fprintf(stderr, "Error creating output sequencer port.\n");
502 }
503 }
504
505 #endif
506
507 //////////////////////////////////////////// Apple Code Only /////////////////////////////////////////
508
509 #if __APPLE__
510
AppleOpen()511 void MidiDevice::AppleOpen()
512 {
513 m_ReadFillIndex = m_ReadReadIndex = 0;
514
515 OSStatus err = 0;
516
517 mMIDISource = NULL;
518 mMIDIClient = NULL;
519 mMIDIDestination = NULL;
520
521 err = MIDIClientCreate(CFSTR("org.pawpal.ssm"), NULL, NULL, &mMIDIClient);
522 if (err) printf("MIDIClientCreate failed returned %d\n", err);
523
524 if (!err) {
525 err = MIDISourceCreate(mMIDIClient, CFSTR("SpiralSynth"), &mMIDISource);
526 if (err) printf("MIDIInputPortCreate failed returned %d\n", err);
527 }
528
529 if (!err) {
530 err = MIDIDestinationCreate(mMIDIClient, CFSTR("SpiralSynth"), sMIDIRead, this, &mMIDIDestination);
531 MIDIObjectSetIntegerProperty(mMIDIDestination, kMIDIPropertyUniqueID, 'SSmP');
532 }
533 }
534
535
AppleClose()536 void MidiDevice::AppleClose()
537 {
538 if (mMIDIDestination)
539 MIDIEndpointDispose(mMIDIDestination);
540 if (mMIDISource)
541 MIDIEndpointDispose(mMIDISource);
542 mMIDISource = NULL;
543 if (mMIDIClient)
544 MIDIClientDispose(mMIDIClient);
545 mMIDIClient = NULL;
546 }
547
AppleWrite(int dummy,unsigned char * outbuffer,int maxlen)548 int MidiDevice::AppleWrite(int dummy, unsigned char *outbuffer, int maxlen)
549 {
550 return 0;
551 }
552
AppleRead(int dummy,unsigned char * outbuffer,int maxlen)553 int MidiDevice::AppleRead(int dummy, unsigned char *outbuffer, int maxlen)
554 {
555 if (!mMIDIClient)
556 return -1;
557 int len = 0;
558 do {
559 while (m_ReadReadIndex == m_ReadFillIndex)
560 usleep(1000); // 1ms
561 int readl = m_ReadFillIndex - m_ReadReadIndex;
562 if (readl < 0)
563 readl += midi_ReadSize; // wrapped
564 while (len < maxlen && readl-- > 0) {
565 int r = m_ReadReadIndex;
566 outbuffer[len++] = m_ReadBuffer[r];
567 r++;
568 m_ReadReadIndex = r % midi_ReadSize;
569 }
570 } while (len < maxlen);
571 return len;
572 }
573
sMIDIRead(const MIDIPacketList * pktlist,void * readProcRefCon,void * srcConnRefCon)574 void MidiDevice::sMIDIRead(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
575 {
576 MidiDevice & t = *((MidiDevice*)readProcRefCon);
577
578 const MIDIPacket *packet = &pktlist->packet[0];
579 for (int i = 0; i < (int)pktlist->numPackets; i++) {
580 const MIDIPacket & p = *packet;
581
582 for (int b = 0; b < p.length; b++) {
583 // printf("%02x ", p.data[b]);
584 int d = t.m_ReadFillIndex;
585 t.m_ReadBuffer[d] = p.data[b];
586 d++;
587 t.m_ReadFillIndex = d % midi_ReadSize;
588 }
589 // printf("\n");
590 packet = MIDIPacketNext(packet);
591 }
592 }
593
594 #endif
595