DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (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 along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #define ALSA_PCM_OLD_HW_PARAMS_API 00021 #define ALSA_PCM_OLD_SW_PARAMS_API 00022 #include <alsa/asoundlib.h> 00023 #include <ctype.h> 00024 #include <string> 00025 #include <sstream> 00026 #define ADDR_DELIM ".:" 00027 00028 #if ((SND_LIB_MINOR >= 6) && (SND_LIB_MAJOR == 0)) || (SND_LIB_MAJOR >= 1) 00029 #define snd_seq_flush_output(x) snd_seq_drain_output(x) 00030 #define snd_seq_set_client_group(x,name) /*nop */ 00031 #define my_snd_seq_open(seqp) snd_seq_open(seqp, "hw", SND_SEQ_OPEN_OUTPUT, 0) 00032 #else 00033 /* SND_SEQ_OPEN_OUT causes oops on early version of ALSA */ 00034 #define my_snd_seq_open(seqp) snd_seq_open(seqp, SND_SEQ_OPEN) 00035 #endif 00036 00037 class MidiHandler_alsa : public MidiHandler { 00038 private: 00039 snd_seq_event_t ev; 00040 snd_seq_t *seq_handle; 00041 int seq_client, seq_port; 00042 int my_client, my_port; 00043 void send_event(int do_flush) { 00044 snd_seq_ev_set_direct(&ev); 00045 snd_seq_ev_set_source(&ev, my_port); 00046 snd_seq_ev_set_dest(&ev, seq_client, seq_port); 00047 00048 snd_seq_event_output(seq_handle, &ev); 00049 if (do_flush) 00050 snd_seq_flush_output(seq_handle); 00051 } 00052 00053 bool parse_addr(const char *arg, int *client, int *port) { 00054 std::string in(arg); 00055 if(in.empty()) return false; 00056 00057 if(in[0] == 's' || in[0] == 'S') { 00058 *client = SND_SEQ_ADDRESS_SUBSCRIBERS; 00059 *port = 0; 00060 return true; 00061 } 00062 00063 if(in.find_first_of(ADDR_DELIM) == std::string::npos) return false; 00064 std::istringstream inp(in); 00065 int val1, val2; char c; 00066 if(!(inp >> val1)) return false; 00067 if(!(inp >> c )) return false; 00068 if(!(inp >> val2)) return false; 00069 *client = val1; *port = val2; 00070 return true; 00071 } 00072 public: 00073 MidiHandler_alsa() : MidiHandler() {}; 00074 const char* GetName(void) { return "alsa"; } 00075 void PlaySysex(Bit8u * sysex,Bitu len) { 00076 snd_seq_ev_set_sysex(&ev, len, sysex); 00077 send_event(1); 00078 } 00079 00080 void PlayMsg(Bit8u * msg) { 00081 ev.type = SND_SEQ_EVENT_OSS; 00082 00083 ev.data.raw32.d[0] = msg[0]; 00084 ev.data.raw32.d[1] = msg[1]; 00085 ev.data.raw32.d[2] = msg[2]; 00086 00087 unsigned char chanID = msg[0] & 0x0F; 00088 switch (msg[0] & 0xF0) { 00089 case 0x80: 00090 snd_seq_ev_set_noteoff(&ev, chanID, msg[1], msg[2]); 00091 send_event(1); 00092 break; 00093 case 0x90: 00094 snd_seq_ev_set_noteon(&ev, chanID, msg[1], msg[2]); 00095 send_event(1); 00096 break; 00097 case 0xA0: 00098 snd_seq_ev_set_keypress(&ev, chanID, msg[1], msg[2]); 00099 send_event(1); 00100 break; 00101 case 0xB0: 00102 snd_seq_ev_set_controller(&ev, chanID, msg[1], msg[2]); 00103 send_event(1); 00104 break; 00105 case 0xC0: 00106 snd_seq_ev_set_pgmchange(&ev, chanID, msg[1]); 00107 send_event(0); 00108 break; 00109 case 0xD0: 00110 snd_seq_ev_set_chanpress(&ev, chanID, msg[1]); 00111 send_event(0); 00112 break; 00113 case 0xE0:{ 00114 long theBend = ((long)msg[1] + (long)(msg[2] << 7)) - 0x2000; 00115 snd_seq_ev_set_pitchbend(&ev, chanID, theBend); 00116 send_event(1); 00117 } 00118 break; 00119 default: 00120 //Maybe filter out FC as it leads for at least one user to crash, but the entire midi stream has not yet been checked. 00121 LOG(LOG_MISC,LOG_DEBUG)("ALSA:Unknown Command: %02X %02X %02X", msg[0],msg[1],msg[2]); 00122 send_event(1); 00123 break; 00124 } 00125 } 00126 00127 void Close(void) { 00128 if (seq_handle) 00129 snd_seq_close(seq_handle); 00130 } 00131 00132 bool Open(const char * conf) { 00133 char var[10]; 00134 unsigned int caps; 00135 bool defaultport = true; //try 17:0. Seems to be default nowadays 00136 00137 // try to use port specified in config file 00138 if (conf && conf[0]) { 00139 safe_strncpy(var, conf, 10); 00140 if (!parse_addr(var, &seq_client, &seq_port)) { 00141 LOG(LOG_MISC,LOG_WARN)("ALSA:Invalid alsa port %s", var); 00142 return false; 00143 } 00144 defaultport = false; 00145 } 00146 // default port if none specified 00147 else if (!parse_addr("65:0", &seq_client, &seq_port)) { 00148 LOG(LOG_MISC,LOG_WARN)("ALSA:Invalid alsa port 65:0"); 00149 return false; 00150 } 00151 00152 if (my_snd_seq_open(&seq_handle)) { 00153 LOG(LOG_MISC,LOG_WARN)("ALSA:Can't open sequencer"); 00154 return false; 00155 } 00156 00157 my_client = snd_seq_client_id(seq_handle); 00158 snd_seq_set_client_name(seq_handle, "DOSBOX"); 00159 snd_seq_set_client_group(seq_handle, "input"); 00160 00161 caps = SND_SEQ_PORT_CAP_READ; 00162 if (seq_client == SND_SEQ_ADDRESS_SUBSCRIBERS) 00163 caps |= SND_SEQ_PORT_CAP_SUBS_READ; 00164 my_port = 00165 snd_seq_create_simple_port(seq_handle, "DOSBOX", caps, 00166 SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION); 00167 if (my_port < 0) { 00168 snd_seq_close(seq_handle); 00169 LOG(LOG_MISC,LOG_WARN)("ALSA:Can't create ALSA port"); 00170 return false; 00171 } 00172 00173 if (seq_client != SND_SEQ_ADDRESS_SUBSCRIBERS) { 00174 /* subscribe to MIDI port */ 00175 if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) < 0) { 00176 if (defaultport) { //if port "65:0" (default) try "17:0" as well 00177 seq_client = 17; seq_port = 0; //Update reported values 00178 if(snd_seq_connect_to(seq_handle,my_port,seq_client,seq_port) < 0) { //Try 128:0 Timidity port as well 00179 // seq_client = 128; seq_port = 0; //Update reported values 00180 // if(snd_seq_connect_to(seq_handle,my_port,seq_client,seq_port) < 0) { 00181 snd_seq_close(seq_handle); 00182 LOG(LOG_MISC,LOG_WARN)("ALSA:Can't subscribe to MIDI port (65:0) nor (17:0)"); 00183 return false; 00184 // } 00185 } 00186 } else { 00187 snd_seq_close(seq_handle); 00188 LOG(LOG_MISC,LOG_WARN)("ALSA:Can't subscribe to MIDI port (%d:%d)", seq_client, seq_port); 00189 return false; 00190 } 00191 } 00192 } 00193 00194 LOG(LOG_MISC,LOG_DEBUG)("ALSA:Client initialised [%d:%d]", seq_client, seq_port); 00195 return true; 00196 } 00197 00198 }; 00199 00200 MidiHandler_alsa Midi_alsa;