1 /* 2 * Copyright (C) 2002-2013 The DOSBox Team 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 20 #define ALSA_PCM_OLD_HW_PARAMS_API 21 #define ALSA_PCM_OLD_SW_PARAMS_API 22 #include <alsa/asoundlib.h> 23 #include <ctype.h> 24 #include <string> 25 #include <sstream> 26 #define ADDR_DELIM ".:" 27 28 #if ((SND_LIB_MINOR >= 6) && (SND_LIB_MAJOR == 0)) || (SND_LIB_MAJOR >= 1) 29 #define snd_seq_flush_output(x) snd_seq_drain_output(x) 30 #define snd_seq_set_client_group(x,name) /*nop */ 31 #define my_snd_seq_open(seqp) snd_seq_open(seqp, "hw", SND_SEQ_OPEN_OUTPUT, 0) 32 #else 33 /* SND_SEQ_OPEN_OUT causes oops on early version of ALSA */ 34 #define my_snd_seq_open(seqp) snd_seq_open(seqp, SND_SEQ_OPEN) 35 #endif 36 37 class MidiHandler_alsa : public MidiHandler { 38 private: 39 snd_seq_event_t ev; 40 snd_seq_t *seq_handle; 41 int seq_client, seq_port; 42 int my_client, my_port; send_event(int do_flush)43 void send_event(int do_flush) { 44 snd_seq_ev_set_direct(&ev); 45 snd_seq_ev_set_source(&ev, my_port); 46 snd_seq_ev_set_dest(&ev, seq_client, seq_port); 47 48 snd_seq_event_output(seq_handle, &ev); 49 if (do_flush) 50 snd_seq_flush_output(seq_handle); 51 } 52 parse_addr(const char * arg,int * client,int * port)53 bool parse_addr(const char *arg, int *client, int *port) { 54 std::string in(arg); 55 if(in.empty()) return false; 56 57 if(in[0] == 's' || in[0] == 'S') { 58 *client = SND_SEQ_ADDRESS_SUBSCRIBERS; 59 *port = 0; 60 return true; 61 } 62 63 if(in.find_first_of(ADDR_DELIM) == std::string::npos) return false; 64 std::istringstream inp(in); 65 int val1, val2; char c; 66 if(!(inp >> val1)) return false; 67 if(!(inp >> c )) return false; 68 if(!(inp >> val2)) return false; 69 *client = val1; *port = val2; 70 return true; 71 } 72 public: MidiHandler_alsa()73 MidiHandler_alsa() : MidiHandler() {}; GetName(void)74 const char* GetName(void) { return "alsa"; } PlaySysex(Bit8u * sysex,Bitu len)75 void PlaySysex(Bit8u * sysex,Bitu len) { 76 snd_seq_ev_set_sysex(&ev, len, sysex); 77 send_event(1); 78 } 79 PlayMsg(Bit8u * msg)80 void PlayMsg(Bit8u * msg) { 81 ev.type = SND_SEQ_EVENT_OSS; 82 83 ev.data.raw32.d[0] = msg[0]; 84 ev.data.raw32.d[1] = msg[1]; 85 ev.data.raw32.d[2] = msg[2]; 86 87 unsigned char chanID = msg[0] & 0x0F; 88 switch (msg[0] & 0xF0) { 89 case 0x80: 90 snd_seq_ev_set_noteoff(&ev, chanID, msg[1], msg[2]); 91 send_event(1); 92 break; 93 case 0x90: 94 snd_seq_ev_set_noteon(&ev, chanID, msg[1], msg[2]); 95 send_event(1); 96 break; 97 case 0xB0: 98 snd_seq_ev_set_controller(&ev, chanID, msg[1], msg[2]); 99 send_event(1); 100 break; 101 case 0xC0: 102 snd_seq_ev_set_pgmchange(&ev, chanID, msg[1]); 103 send_event(0); 104 break; 105 case 0xD0: 106 snd_seq_ev_set_chanpress(&ev, chanID, msg[1]); 107 send_event(0); 108 break; 109 case 0xE0:{ 110 long theBend = ((long)msg[1] + (long)(msg[2] << 7)) - 0x2000; 111 snd_seq_ev_set_pitchbend(&ev, chanID, theBend); 112 send_event(1); 113 } 114 break; 115 default: 116 LOG(LOG_MISC,LOG_WARN)("ALSA:Unknown Command: %08lx", (long)msg); 117 send_event(1); 118 break; 119 } 120 } 121 Close(void)122 void Close(void) { 123 if (seq_handle) 124 snd_seq_close(seq_handle); 125 } 126 Open(const char * conf)127 bool Open(const char * conf) { 128 char var[10]; 129 unsigned int caps; 130 bool defaultport = true; //try 17:0. Seems to be default nowadays 131 132 // try to use port specified in config file 133 if (conf && conf[0]) { 134 safe_strncpy(var, conf, 10); 135 if (!parse_addr(var, &seq_client, &seq_port)) { 136 LOG_MSG("ALSA:Invalid alsa port %s", var); 137 return false; 138 } 139 defaultport = false; 140 } 141 // default port if none specified 142 else if (!parse_addr("65:0", &seq_client, &seq_port)) { 143 LOG_MSG("ALSA:Invalid alsa port 65:0"); 144 return false; 145 } 146 147 if (my_snd_seq_open(&seq_handle)) { 148 LOG_MSG("ALSA:Can't open sequencer"); 149 return false; 150 } 151 152 my_client = snd_seq_client_id(seq_handle); 153 snd_seq_set_client_name(seq_handle, "DOSBOX"); 154 snd_seq_set_client_group(seq_handle, "input"); 155 156 caps = SND_SEQ_PORT_CAP_READ; 157 if (seq_client == SND_SEQ_ADDRESS_SUBSCRIBERS) 158 caps = ~SND_SEQ_PORT_CAP_SUBS_READ; 159 my_port = 160 snd_seq_create_simple_port(seq_handle, "DOSBOX", caps, 161 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); 162 if (my_port < 0) { 163 snd_seq_close(seq_handle); 164 LOG_MSG("ALSA:Can't create ALSA port"); 165 return false; 166 } 167 168 if (seq_client != SND_SEQ_ADDRESS_SUBSCRIBERS) { 169 /* subscribe to MIDI port */ 170 if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) < 0) { 171 if (defaultport) { //if port "65:0" (default) try "17:0" as well 172 seq_client = 17; seq_port = 0; //Update reported values 173 if(snd_seq_connect_to(seq_handle,my_port,seq_client,seq_port) < 0) { //Try 128:0 Timidity port as well 174 // seq_client = 128; seq_port = 0; //Update reported values 175 // if(snd_seq_connect_to(seq_handle,my_port,seq_client,seq_port) < 0) { 176 snd_seq_close(seq_handle); 177 LOG_MSG("ALSA:Can't subscribe to MIDI port (65:0) nor (17:0)"); 178 return false; 179 // } 180 } 181 } else { 182 snd_seq_close(seq_handle); 183 LOG_MSG("ALSA:Can't subscribe to MIDI port (%d:%d)", seq_client, seq_port); 184 return false; 185 } 186 } 187 } 188 189 LOG_MSG("ALSA:Client initialised [%d:%d]", seq_client, seq_port); 190 return true; 191 } 192 193 }; 194 195 MidiHandler_alsa Midi_alsa; 196