/* * Copyright (C) 2000 David Griffiths * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Midi.h" #include "unistd.h" #include "sys/types.h" #include "signal.h" #include "pthread.h" using namespace spiralcore; static const int MIDI_SCANBUFSIZE=256; static const int MIDI_KEYOFFSET=0; static const unsigned char STATUS_START = 0x80; static const unsigned char STATUS_NOTE_OFF = 0x80; static const unsigned char STATUS_NOTE_ON = 0x90; static const unsigned char STATUS_AFTERTOUCH = 0xa0; static const unsigned char STATUS_CONTROL_CHANGE = 0xb0; static const unsigned char STATUS_PROG_CHANGE = 0xc0; static const unsigned char STATUS_CHANNEL_PRESSURE = 0xd0; static const unsigned char STATUS_PITCH_WHEEL = 0xe0; static const unsigned char STATUS_END = 0xf0; static const unsigned char SYSEX_START = 0xf0; static const unsigned char SYSEX_TERMINATOR = 0xf7; static const unsigned char MIDI_CLOCK = 0xf8; static const unsigned char ACTIVE_SENSE = 0xfe; static int NKEYS = 30; MidiDevice *MidiDevice::m_Singleton; string MidiDevice::m_DeviceName; string MidiDevice::m_AppName; #if __APPLE__ #define read AppleRead #endif void MidiDevice::Init(const string &name, Type t) { if (!m_Singleton) { m_AppName=name; m_Singleton=new MidiDevice(t); } } MidiDevice::MidiDevice(Type t) : m_Poly(1), m_Clock(1.0f), m_ClockCount(0), m_Type(t) { Open(t); } MidiDevice::~MidiDevice() { #ifdef ALSA_MIDI snd_seq_close (seq_handle); #else Close(); #endif } void MidiDevice::Close() { #if __APPLE__ AppleClose(); #else pthread_mutex_lock(m_Mutex); pthread_cancel(m_MidiReader); pthread_mutex_unlock(m_Mutex); pthread_mutex_destroy(m_Mutex); close(m_MidiFd); close(m_MidiWrFd); cerr<<"Closed midi device"<15) { cerr<<"GetEvent: Invalid Midi device "<15) { cerr<<"SendEvent: Invalid Midi device "<=STATUS_END && *c!=MIDI_CLOCK); } // collect events deals with the byte level messages, and sorts // and filters them into distinct messages we can handle easily void MidiDevice::CollectEvents() { unsigned char buf[1]; int count,n,nn; bool MessageSent; unsigned char data[3],last=0; // constantly scan for relevent input, // and write it to the pipe // filters out unhandled messages, and attempts to build // coherent messages to send to the midi handler bool InSysex=false; for(;;) { ReadByte(buf); if (*buf==MIDI_CLOCK) { m_ClockCount++; if (m_ClockCount==6) { m_Clock=-m_Clock; m_ClockCount=0; } } else if (*buf>=STATUS_START) // we've got a status byte { if (*buf==SYSEX_TERMINATOR) InSysex=false; // find out if it's an opcode if(*buf>=STATUS_START && *buf<=STATUS_END) { InSysex=false; last=data[0]=*buf; if (data[0]>=STATUS_PROG_CHANGE && data[0]=STATUS_PROG_CHANGE && data[0]= STATUS_NOTE_OFF && midi[0] < STATUS_NOTE_ON) { MessageType=MidiEvent::OFF; Note=midi[1]-MIDI_KEYOFFSET; EventDevice=midi[0]-STATUS_NOTE_OFF; } // note on else if (midi[0] >= STATUS_NOTE_ON && midi[0] < STATUS_AFTERTOUCH) { Volume = midi[2]; // cope with Roland equipment, where note on's // with zero velocity are sent as note offs. if (Volume) MessageType=MidiEvent::ON; else MessageType=MidiEvent::OFF; Note=midi[1]-MIDI_KEYOFFSET; EventDevice=midi[0]-STATUS_NOTE_ON; } // aftertouch else if (midi[0] >= STATUS_AFTERTOUCH && midi[0] < STATUS_CONTROL_CHANGE) { MessageType=MidiEvent::AFTERTOUCH; Note=midi[1]-MIDI_KEYOFFSET; Volume=midi[2]; EventDevice=midi[0]-STATUS_AFTERTOUCH; } // parameter else if (midi[0] >= STATUS_CONTROL_CHANGE && midi[0] < STATUS_PROG_CHANGE) { MessageType=MidiEvent::PARAMETER; Note=midi[1]; Volume=midi[2]; EventDevice=midi[0]-STATUS_CONTROL_CHANGE; } // channel pressure else if (midi[0] >= STATUS_CHANNEL_PRESSURE && midi[0] < STATUS_PITCH_WHEEL) { MessageType=MidiEvent::CHANNELPRESSURE; Volume=midi[1]; EventDevice=midi[0]-STATUS_CHANNEL_PRESSURE; } // note pitchbend else if (midi[0] >= STATUS_PITCH_WHEEL && midi[0] < STATUS_END) { MessageType=MidiEvent::PITCHBEND; // should probably take the first byte into account too? Volume=midi[2]; EventDevice=midi[0]-STATUS_PITCH_WHEEL; } if (EventDevice<0 || EventDevice>15) { cerr<<"Error - Midi device "< 0) { for (l1 = 0; l1 < seq_nfds; l1++) { if (pfds[l1].revents > 0) { snd_seq_event_t *ev; int l1; MidiEvent::type MessageType=MidiEvent::NONE; int Volume=0,Note=0,EventDevice=0; do { snd_seq_event_input(seq_handle, &ev); if ((ev->type == SND_SEQ_EVENT_NOTEON) && (ev->data.note.velocity == 0)) { ev->type = SND_SEQ_EVENT_NOTEOFF; } switch (ev->type) { case SND_SEQ_EVENT_PITCHBEND: MessageType=MidiEvent::CHANNELPRESSURE; Volume = (char)((ev->data.control.value / 8192.0)*256); break; case SND_SEQ_EVENT_CONTROLLER: MessageType=MidiEvent::PARAMETER; Note = ev->data.control.param; Volume = ev->data.control.value; break; case SND_SEQ_EVENT_NOTEON: MessageType=MidiEvent::ON; EventDevice = ev->data.control.channel; Note = ev->data.note.note; Volume = ev->data.note.velocity; break; case SND_SEQ_EVENT_NOTEOFF: MessageType=MidiEvent::ON; EventDevice = ev->data.control.channel; Note = ev->data.note.note; break; } pthread_mutex_lock(m_Mutex); m_EventVec[EventDevice].push(MidiEvent(MessageType,Note,Volume)); pthread_mutex_unlock(m_Mutex); snd_seq_free_event(ev); } while (snd_seq_event_input_pending(seq_handle, 0) > 0); } } } } } snd_seq_t *MidiDevice::AlsaOpen(Type t) { snd_seq_t *seq_handle; int client_id, port_id; if (t==WRITE) { if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_OUTPUT, 0) < 0) { fprintf(stderr, "Error opening ALSA sequencer.\n"); exit(1); } snd_seq_set_client_name(seq_handle, m_AppName.c_str()); client_id = snd_seq_client_id(seq_handle); if ((port_id = snd_seq_create_simple_port(seq_handle, m_AppName.c_str(), SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION) < 0)) { fprintf(stderr, "Error creating sequencer port.\n"); } } else { if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, 0) < 0) { fprintf(stderr, "Error opening ALSA sequencer.\n"); exit(1); } snd_seq_set_client_name(seq_handle, m_AppName.c_str()); client_id = snd_seq_client_id(seq_handle); if ((port_id = snd_seq_create_simple_port(seq_handle, m_AppName.c_str(), SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION) < 0)) { fprintf(stderr, "Error creating sequencer port.\n"); } } return(seq_handle); } #endif #if __APPLE__ void MidiDevice::AppleOpen() { m_ReadFillIndex = m_ReadReadIndex = 0; OSStatus err = 0; mMIDISource = NULL; mMIDIClient = NULL; mMIDIDestination = NULL; err = MIDIClientCreate(CFSTR("org.pawpal.ssm"), NULL, NULL, &mMIDIClient); if (err) printf("MIDIClientCreate failed returned %d\n", err); if (!err) { err = MIDISourceCreate(mMIDIClient, CFSTR("SpiralSynth"), &mMIDISource); if (err) printf("MIDIInputPortCreate failed returned %d\n", err); } if (!err) { err = MIDIDestinationCreate(mMIDIClient, CFSTR("SpiralSynth"), sMIDIRead, this, &mMIDIDestination); MIDIObjectSetIntegerProperty(mMIDIDestination, kMIDIPropertyUniqueID, 'SSmP'); } } void MidiDevice::AppleClose() { if (mMIDIDestination) MIDIEndpointDispose(mMIDIDestination); if (mMIDISource) MIDIEndpointDispose(mMIDISource); mMIDISource = NULL; if (mMIDIClient) MIDIClientDispose(mMIDIClient); mMIDIClient = NULL; } int MidiDevice::AppleWrite(int dummy, unsigned char *outbuffer, int maxlen) { return 0; } int MidiDevice::AppleRead(int dummy, unsigned char *outbuffer, int maxlen) { if (!mMIDIClient) return -1; int len = 0; do { while (m_ReadReadIndex == m_ReadFillIndex) usleep(1000); // 1ms int readl = m_ReadFillIndex - m_ReadReadIndex; if (readl < 0) readl += midi_ReadSize; // wrapped while (len < maxlen && readl-- > 0) { int r = m_ReadReadIndex; outbuffer[len++] = m_ReadBuffer[r]; r++; m_ReadReadIndex = r % midi_ReadSize; } } while (len < maxlen); return len; } void MidiDevice::sMIDIRead(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon) { MidiDevice & t = *((MidiDevice*)readProcRefCon); const MIDIPacket *packet = &pktlist->packet[0]; for (int i = 0; i < (int)pktlist->numPackets; i++) { const MIDIPacket & p = *packet; for (int b = 0; b < p.length; b++) { // printf("%02x ", p.data[b]); int d = t.m_ReadFillIndex; t.m_ReadBuffer[d] = p.data[b]; d++; t.m_ReadFillIndex = d % midi_ReadSize; } // printf("\n"); packet = MIDIPacketNext(packet); } } #endif