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