/* * Copyleft (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 "NoteTable.h" #include "unistd.h" #include "sys/types.h" #include "signal.h" #include "SpiralInfo.h" static const int MIDI_SCANBUFSIZE=256; static const int MIDI_KEYOFFSET=0; MidiDevice::MidiDevice() { Init(); } MidiDevice::~MidiDevice() { Close(); } void MidiDevice::Close() { if (!SpiralInfo::WANTMIDI) return; close(m_MidiFd); kill(m_ChildPID,9); } void MidiDevice::Init() { if (!SpiralInfo::WANTMIDI) return; m_MidiFd = open(SpiralInfo::MIDIFILE.c_str(),O_RDONLY); pipe(m_Pipefd); fcntl(m_Pipefd[0],F_SETFL,O_NONBLOCK); m_ChildPID=fork(); if (!m_ChildPID) { CollectEvents(); } } 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 for(;;) { read(m_MidiFd,buf,1); if (*buf!=248) // odd code that gets sent occasionally { if (*buf>=0x80) // we've got a opcode { last=data[0]=*buf; // find out what the opcode means if(data[0]>=0x90 && data[0]<=0x9f) // note on event { // get the next two bytes read(m_MidiFd,&data[1],1); read(m_MidiFd,&data[2],1); write(m_Pipefd[1],data,3); } else if(data[0]>=0x80 && data[0]<=0x8f) // note off event { // get the next two bytes read(m_MidiFd,&data[1],1); read(m_MidiFd,&data[2],1); write(m_Pipefd[1],data,3); } else if(data[0]>=0xe0 && data[0]<=0xef) // pitch wheel event { // get the next two bytes read(m_MidiFd,&data[1],1); read(m_MidiFd,&data[2],1); write(m_Pipefd[1],data,3); } else if(data[0]>=0xb0 && data[0]<=0xbf) // parameter event { // get the next two byte read(m_MidiFd,&data[1],1); read(m_MidiFd,&data[2],1); write(m_Pipefd[1],data,3); } else { last=0; // we don't know this opcode } } else // more data { if (last!=0) { data[0]=last; data[1]=*buf; // get the last byte if it's not a pressure message if(data[0]>=0xd0 && data[0]<=0xdf) { data[2]=0; } else { read(m_MidiFd,&data[2],1); } write(m_Pipefd[1],data,3); } } } } } MidiEvent MidiDevice::GetEvent() { // This is a really quick hack based on the outputs from the // /dev/midi device. Doesn't really cope with anything difficult, // such as events being split by progam changes etc etc. if (!SpiralInfo::WANTMIDI) return MidiEvent(MidiEvent::NONE,0,0,0); unsigned char midi[3]; int count=read(m_Pipefd[0],midi,3); // no message if (count<=0) return MidiEvent(MidiEvent::NONE,0,0,0); MidiEvent::type MessageType=MidiEvent::NONE; int Volume=0,Octave=0,Note=0; // todo: assumes midi channel 0 of 16 switch (midi[0]) { case 0x80: // note off { MessageType=MidiEvent::OFF; Octave=(midi[1]-MIDI_KEYOFFSET)/12; // force range of our synth if (Octave<0) Octave=0; if (Octave>10) Octave=10; Note=(midi[1]-MIDI_KEYOFFSET)%12; break; } case 0x90: // note on { 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; Octave=(midi[1]-MIDI_KEYOFFSET)/12; // force range of our synth if (Octave<0) Octave=0; if (Octave>10) Octave=10; Note=(midi[1]-MIDI_KEYOFFSET)%12; break; } case 0xb0: // parameter { MessageType=MidiEvent::PARAMETER; Octave=0; Note=midi[1]; Volume=midi[2]; break; } case 0xe0: // note pitchbend { MessageType=MidiEvent::PITCHBEND; // should probably take the first byte into account too. Volume=midi[2]; break; } } return MidiEvent(MessageType,Octave,Note,Volume); }