ALSAMidiDriver.cpp

Go to the documentation of this file.
00001 /*
00002 Copyright (C) 2004-2005  The Pentagram Team
00003 
00004 This program is free software; you can redistribute it and/or
00005 modify it under the terms of the GNU General Public License
00006 as published by the Free Software Foundation; either version 2
00007 of the License, or (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00017 */
00018 
00019 /*
00020   ALSA MIDI driver
00021   Adapted from ScummVM's backends/midi/alsa.cpp
00022   (which is in turn
00023    "Mostly cut'n'pasted from Virtual Tiny Keyboard (vkeybd) by Takashi Iwai" )
00024 */
00025 
00026 #include "pent_include.h"
00027 #include "ALSAMidiDriver.h"
00028 
00029 #ifdef USE_ALSA_MIDI
00030 
00031 const MidiDriver::MidiDriverDesc ALSAMidiDriver::desc =
00032                 MidiDriver::MidiDriverDesc ("alsa", createInstance);
00033 
00034 
00035 
00036 
00037 #if SND_LIB_MAJOR >= 1 || SND_LIB_MINOR >= 6
00038 #define snd_seq_flush_output(x) snd_seq_drain_output(x)
00039 #define snd_seq_set_client_group(x,name)        /*nop */
00040 #define my_snd_seq_open(seqp) snd_seq_open(seqp, "hw", SND_SEQ_OPEN_OUTPUT, 0)
00041 #else
00042 /* SND_SEQ_OPEN_OUT causes oops on early version of ALSA */
00043 #define my_snd_seq_open(seqp) snd_seq_open(seqp, SND_SEQ_OPEN)
00044 #endif
00045 
00046 #define ALSA_PORT "65:0"
00047 #define ADDR_DELIM ".:"
00048 
00049 
00050 ALSAMidiDriver::ALSAMidiDriver()
00051  : isOpen(false), seq_handle(0), seq_client(0), seq_port(0),
00052    my_client(0), my_port(0)
00053 {
00054         memset(&ev, 0, sizeof(ev));
00055 }
00056 
00057 int ALSAMidiDriver::open() {
00058         std::string arg;
00059         unsigned int caps;
00060 
00061         if (isOpen)
00062                 return -1;
00063 
00064         arg = getConfigSetting("alsa_port", ALSA_PORT);
00065 
00066         if (parse_addr(arg, &seq_client, &seq_port) < 0) {
00067                 perr << "ALSAMidiDriver: Invalid port: " << arg << std::endl;
00068                 return -1;
00069         }
00070         
00071         if (my_snd_seq_open(&seq_handle)) {
00072                 perr << "ALSAMidiDriver: Can't open sequencer" << std::endl;
00073                 return -1;
00074         }
00075 
00076         isOpen = true;
00077         
00078         my_client = snd_seq_client_id(seq_handle);
00079         snd_seq_set_client_name(seq_handle, "PENTAGRAM");
00080         snd_seq_set_client_group(seq_handle, "input");
00081         
00082         caps = SND_SEQ_PORT_CAP_READ;
00083         if (seq_client == SND_SEQ_ADDRESS_SUBSCRIBERS)
00084                 caps = ~SND_SEQ_PORT_CAP_SUBS_READ;
00085         my_port =
00086                 snd_seq_create_simple_port(seq_handle, "PENTAGRAM", caps,
00087                                                                    SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
00088         if (my_port < 0) {
00089                 snd_seq_close(seq_handle);
00090                 isOpen = false;
00091                 perr << "ALSAMidiDriver: Can't create port" << std::endl;
00092                 return -1;
00093         }
00094 
00095         if (seq_client != SND_SEQ_ADDRESS_SUBSCRIBERS) {
00096                 /* subscribe to MIDI port */
00097                 if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) < 0) {
00098                         snd_seq_close(seq_handle);
00099                         isOpen = false;
00100                         perr << "ALSAMidiDriver: "
00101                                  << "Can't subscribe to MIDI port (" << seq_client
00102                                  << ":" << seq_port << ")" << std::endl;
00103                         return -1;
00104                 }
00105         }
00106 
00107         pout << "ALSA client initialised [" << seq_client << ":"
00108                  << seq_port << "]" << std::endl;
00109 
00110         return 0;
00111 }
00112 
00113 void ALSAMidiDriver::close() {
00114         isOpen = false;
00115         if (seq_handle)
00116                 snd_seq_close(seq_handle);
00117 }
00118 
00119 void ALSAMidiDriver::send(uint32 b) {
00120         unsigned int midiCmd[4];
00121         ev.type = SND_SEQ_EVENT_OSS;
00122 
00123         midiCmd[3] = (b & 0xFF000000) >> 24;
00124         midiCmd[2] = (b & 0x00FF0000) >> 16;
00125         midiCmd[1] = (b & 0x0000FF00) >> 8;
00126         midiCmd[0] = (b & 0x000000FF);
00127         ev.data.raw32.d[0] = midiCmd[0];
00128         ev.data.raw32.d[1] = midiCmd[1];
00129         ev.data.raw32.d[2] = midiCmd[2];
00130 
00131         unsigned char chanID = midiCmd[0] & 0x0F;
00132         switch (midiCmd[0] & 0xF0) {
00133         case 0x80:
00134                 snd_seq_ev_set_noteoff(&ev, chanID, midiCmd[1], midiCmd[2]);
00135                 send_event(1);
00136                 break;
00137         case 0x90:
00138                 snd_seq_ev_set_noteon(&ev, chanID, midiCmd[1], midiCmd[2]);
00139                 send_event(1);
00140                 break;
00141         case 0xB0:
00142                 /* is it this simple ? Wow... */
00143                 snd_seq_ev_set_controller(&ev, chanID, midiCmd[1], midiCmd[2]);
00144                 send_event(1);
00145                 break;
00146         case 0xC0:
00147                 snd_seq_ev_set_pgmchange(&ev, chanID, midiCmd[1]);
00148                 send_event(0);
00149                 break;
00150         case 0xD0:
00151                 snd_seq_ev_set_chanpress(&ev, chanID, midiCmd[1]);
00152                 send_event(0);
00153                 break;
00154         case 0xE0:{
00155                         // long theBend = ((((long)midiCmd[1] + (long)(midiCmd[2] << 7))) - 0x2000) / 4;
00156                         // snd_seq_ev_set_pitchbend(&ev, chanID, theBend);
00157                         long theBend = ((long)midiCmd[1] + (long)(midiCmd[2] << 7)) - 0x2000;
00158                         snd_seq_ev_set_pitchbend(&ev, chanID, theBend);
00159                         send_event(1);
00160                 }
00161                 break;
00162 
00163         default:
00164                 perr << "ALSAMidiDriver: Unknown Command: "
00165                          << std::hex << (int)b << std::dec << std::endl;
00166                 /* I don't know if this works but, well... */
00167                 send_event(1);
00168                 break;
00169         }
00170 }
00171 
00172 void ALSAMidiDriver::send_sysex(uint8 status,const uint8 *msg,uint16 length) {
00173         unsigned char buf[1024];
00174 
00175         if (length > 511) {
00176                 perr << "ALSAMidiDriver: "
00177                          << "Cannot send SysEx block - data too large" << std::endl;
00178                 return;
00179         }
00180         buf[0] = status;
00181         memcpy(buf + 1, msg, length);
00182         snd_seq_ev_set_sysex(&ev, length + 1, &buf);
00183         send_event(1);
00184 }
00185 
00186 // static
00187 int ALSAMidiDriver::parse_addr(std::string _arg, int *client, int *port) {
00188         const char* arg = _arg.c_str();
00189         char *p;
00190 
00191         if (isdigit(*arg)) {
00192                 if ((p = strpbrk(arg, ADDR_DELIM)) == NULL)
00193                         return -1;
00194                 *client = atoi(arg);
00195                 *port = atoi(p + 1);
00196         } else {
00197                 if (*arg == 's' || *arg == 'S') {
00198                         *client = SND_SEQ_ADDRESS_SUBSCRIBERS;
00199                         *port = 0;
00200                 } else
00201                         return -1;
00202         }
00203         return 0;
00204 }
00205 
00206 void ALSAMidiDriver::send_event(int do_flush) {
00207         snd_seq_ev_set_direct(&ev);
00208         snd_seq_ev_set_source(&ev, my_port);
00209         snd_seq_ev_set_dest(&ev, seq_client, seq_port);
00210 
00211         snd_seq_event_output(seq_handle, &ev);
00212         if (do_flush)
00213                 snd_seq_flush_output(seq_handle);
00214 }
00215 
00216 #endif

Generated on Fri Jul 27 22:27:09 2007 for pentagram by  doxygen 1.4.7