From 7b7fd8dfa001060b07ce9a19ee6e513a8a661078 Mon Sep 17 00:00:00 2001 From: bbond007 Date: Wed, 15 Jul 2020 21:25:51 -0400 Subject: [PATCH 1/4] Refactor modem stuff out of main.c to modem.c --- Makefile | 2 +- config.h | 22 +- main.c | 1231 ++++++++--------------------------------------------- midilink | Bin 61496 -> 61528 bytes misc.c | 2 + mlinkutil | Bin 18176 -> 18296 bytes modem.c | 957 +++++++++++++++++++++++++++++++++++++++++ modem.h | 17 + 8 files changed, 1160 insertions(+), 1071 deletions(-) create mode 100644 modem.c create mode 100644 modem.h diff --git a/Makefile b/Makefile index 206d0da..4018dd1 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ STRIP=arm-linux-gnueabihf-strip CCFLAGS=-Ialsa/include -Lalsa/lib -Ofast -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=hard -ftree-vectorize -funsafe-math-optimizations LDFLAGS=-lasound -lm -pthread all : - $(CC) $(CCFLAGS) $(LDFLAGS) main.c serial.c serial2.c misc.c udpsock.c tcpsock.c alsa.c ini.c directory.c modem_snd.c -o midilink + $(CC) $(CCFLAGS) $(LDFLAGS) main.c modem.c serial.c serial2.c misc.c udpsock.c tcpsock.c alsa.c ini.c directory.c modem_snd.c -o midilink $(STRIP) midilink $(CC) $(CCFLAGS) mlinkutil.c misc.c serial2.c tcpsock.c -o mlinkutil $(STRIP) mlinkutil diff --git a/config.h b/config.h index 1032072..986af5a 100644 --- a/config.h +++ b/config.h @@ -9,21 +9,20 @@ // Thanks to https://ccrma.stanford.edu/~craig/articles/linuxmidi/ for the // excellent MIDI programming examples. // -// "/dev/sequencer" does not seem to work for input -static char * midiLinkINI = "/media/fat/linux/MidiLink.INI"; -static char * midiLinkDIR = "/media/fat/linux/MidiLink.DIR"; -static char * serialDevice = "/dev/ttyS1"; -static char * serialDeviceUSB = "/dev/ttyUSB0"; -static char * helloStr = "MiSTer MidiLink 2.9d BB7"; +char * midiLinkINI = "/media/fat/linux/MidiLink.INI"; +char * midiLinkDIR = "/media/fat/linux/MidiLink.DIR"; +char * serialDevice = "/dev/ttyS1"; +char * helloStr = "MiSTer MidiLink 3.0 BB7"; +char * PCMDevice = "/dev/snd/pcmC0D0p"; +int CPUMASK = 1; +static char * MrAudioDevice = "/dev/MrAudio"; static unsigned char test_note[3] = {0x90, 60, 127}; static char * midiINDevice = "/dev/midi2"; static char * midiDevice = "/dev/midi1"; -static char * PCMDevice = "/dev/snd/pcmC0D0p"; -static char * MrAudioDevice = "/dev/MrAudio"; -static int CPUMASK = 1; - -static char all_notes_off[] = +static char * serialDeviceUSB = "/dev/ttyUSB0"; + +char all_notes_off[] = { 0xb0, 0x7b, 0x00, 0xb0, 0x40, 0x00, 0xb1, 0x7b, 0x00, 0xb1, 0x40, 0x00, 0xb2, 0x7b, 0x00, 0xb2, 0x40, 0x00, 0xb3, 0x7b, 0x00, 0xb3, 0x40, 0x00, 0xb4, 0x7b, 0x00, 0xb4, 0x40, 0x00, 0xb5, 0x7b, 0x00, 0xb5, 0x40, 0x00, @@ -33,6 +32,7 @@ static char all_notes_off[] = 0xbf, 0x7b, 0x00, 0xbf, 0x40, 0x00 }; +int sizeof_all_notes_off = sizeof(all_notes_off); diff --git a/main.c b/main.c index cf412ab..fed4847 100644 --- a/main.c +++ b/main.c @@ -33,74 +33,55 @@ along with this program. If not, see . #include "tcpsock.h" #include "alsa.h" #include "ini.h" -#include "directory.h" -#include "modem_snd.h" +#include "modem.h" -#define DEFAULT_MODEMSOUND TRUE -#define DEFAULT_modemVolume -1 -#define DEFAULT_TCPAsciiTrans AsciiNoTrans -#define DEFAULT_TCPTermRows 22 #define DEFAULT_TCPFlow -1 #define DEFAULT_TCPDTR 1 -#define DEFAULT_TCPQuiet 0 enum MODE {ModeUSBMIDI, ModeTCP, ModeUDP, ModeUSBSER, ModeMUNT, ModeMUNTGM, ModeFSYNTH, ModeUDPMUNT, ModeUDPMUNTGM, ModeUDPFSYNTH}; -int MIDI_DEBUG = TRUE; static enum MODE mode = ModeUSBMIDI; -static int fdSerial = -1; static int fdSerialUSB = -1; -static int fdMidi = -1; -static int fdMidiIN = -1; -static int socket_in = -1; -static int socket_out = -1; -static int socket_lst = -1; -static int baudRate = -1; +static int fdMidi = -1; +static int fdMidiIN = -1; + +int MIDI_DEBUG = TRUE; +int socket_lst = -1; +int socket_out = -1; +int fdSerial = -1; +int socket_in = -1; +int baudRate = -1; char MT32LCDMsg[21] = "MiSTer MidiLink! BB7"; -char MP3Path[500] = "/media/fat/MP3"; -char MIDIPath[500] = "/media/fat/MIDI"; -char downloadPath[500] = "/media/fat"; -char uploadPath[100] = "/media/fat/UPLOAD"; -char fsynthSoundFont [150] = "/media/fat/linux/soundfonts/SC-55.sf2"; +char fsynthSoundFont [150] = "/media/fat/linux/soundfonts/SC-55.sf2"; char MUNTRomPath[150] = "/media/fat/linux/mt32-rom-data"; -char modemConnectSndWAV[50] = ""; -char modemDialSndWAV[50] = ""; -char modemRingSndWAV[50] = ""; -char UDPServer [100] = ""; -char mixerControl[20] = "Master"; -char MUNTOptions[30] = ""; -int MP3Volume = -1; -int muntVolume = -1; -int fsynthVolume = -1; -int modemVolume = DEFAULT_modemVolume; -int midilinkPriority = 0; +char UDPServer [100] = ""; +char mixerControl[20] = "Master"; +char MUNTOptions[30] = ""; +char USBSerModule[100] = ""; +int muntVolume = -1; +int fsynthVolume = -1; +int midilinkPriority = 0; int UDPBaudRate = -1; int TCPBaudRate = -1; int UDPBaudRate_alt = -1; int TCPBaudRate_alt = -1; -int MIDIBaudRate = -1; +int MIDIBaudRate = -1; int USBSerBaudRate = -1; -char USBSerModule[100] = ""; -enum ASCIITRANS TCPAsciiTrans = DEFAULT_TCPAsciiTrans; int TCPFlow = DEFAULT_TCPFlow; int TCPDTR = DEFAULT_TCPDTR; int UDPFlow = -1; -int MODEMSOUND = DEFAULT_MODEMSOUND; -int TCPQuiet = DEFAULT_TCPQuiet; -int TCPATHDelay = 900; int MUNTCPUMask = -1; int FSYNTHCPUMask = -1; -enum SOFTSYNTH TCPSoftSynth = FluidSynth; -unsigned int TCPTermRows = DEFAULT_TCPTermRows; -unsigned int DELAYSYSEX = FALSE; -unsigned int UDPServerPort = 1999; -unsigned int TCPServerPort = 23; +unsigned int DELAYSYSEX = FALSE; +unsigned int UDPServerPort = 1999; +unsigned int TCPServerPort = 23; unsigned int UDPServerFilterIP = FALSE; -static pthread_t midiInThread; -static pthread_t midiINInThread; -static pthread_t socketInThread; + +static pthread_t midiInThread; +static pthread_t midiINInThread; +static pthread_t socketInThread; static pthread_t socketLstThread; -static pthread_t serialInThread; +static pthread_t serialInThread; /////////////////////////////////////////////////////////////////////////////////////// // @@ -187,42 +168,6 @@ int start_fsynth() return midiPort; } -/////////////////////////////////////////////////////////////////////////////////////// -// -// void killall_mpg123() -// -void killall_mpg123(int delay) -{ - misc_print(0, "Killing --> mpg123\n"); - system("killall -q mpg123"); - if(delay) - sleep(delay); -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void killall_aplaymidi() -// -void killall_aplaymidi(int delay) -{ - misc_print(0, "Killing --> aplaymidi\n"); - system("killall -q aplaymidi"); - if(delay) - sleep(delay); -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void killall_aplaymidi() -// -void killall_aplay(int delay) -{ - misc_print(0, "Killing --> aplay\n"); - system("killall -q aplay"); - if(delay) - sleep(delay); -} - /////////////////////////////////////////////////////////////////////////////////////// // // void show_debug_buf(char * descr, char * buf, int bufLen) @@ -244,78 +189,6 @@ void show_debug_buf(char * descr, char * buf, int bufLen) } } -/////////////////////////////////////////////////////////////////////////////////////// -// -// void play_conenct_sound(char * tmp) -// -// -void play_connect_sound(char * tmp) -{ - if (MODEMSOUND) - { - killall_aplaymidi(0); - killall_softsynth(0); - killall_mpg123(0); - killall_aplay(0); - if(strlen(modemConnectSndWAV) > 0 && misc_check_file(modemConnectSndWAV)) - { - misc_print(1, "Playing WAV --> '%s'\n", modemConnectSndWAV); - sprintf(tmp, "aplay %s", modemConnectSndWAV); - system(tmp); - } - else - modem_snd("C"); - } -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void play_conenct_sound(char * tmp) -// -// -void play_ring_sound(char * tmp) -{ - if (MODEMSOUND) - { - killall_aplaymidi(0); - killall_softsynth(0); - killall_mpg123(0); - killall_aplay(0); - if(strlen(modemRingSndWAV) > 0 && misc_check_file(modemRingSndWAV)) - { - misc_print(1, "Playing WAV --> '%s'\n", modemRingSndWAV); - sprintf(tmp, "aplay %s", modemRingSndWAV); - system(tmp); - } - else - modem_snd("R"); - } -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void play_dial_sound(char * tmp, char * ipAddr) -// -// -void play_dial_sound(char * tmp, char * ipAddr) -{ - if (MODEMSOUND) - { - killall_aplaymidi(0); - killall_softsynth(0); - killall_mpg123(0); - killall_aplay(0); - if (strlen(modemDialSndWAV) > 0 && misc_check_file(modemDialSndWAV)) - { - misc_print(1, "Playing WAV --> '%s'\n", modemDialSndWAV); - sprintf(tmp, "aplay %s", modemDialSndWAV); - system(tmp); - } - else - modem_snd(ipAddr); - } -} - /////////////////////////////////////////////////////////////////////////////////////// // // write_[sequencer]_packet() @@ -327,884 +200,6 @@ void write_alsa_packet(char * buf, int bufLen) show_debug_buf("SEQU OUT ", buf, bufLen); } -/////////////////////////////////////////////////////////////////////////////////////// -// -// void * tcplst_thread_function(void * x) -// Thread function for TCP Listener input -// -void * tcplst_thread_function (void * x) -{ - unsigned char buf[100]; - int rdLen; - - do - { - socket_in = tcpsock_accept(socket_lst); - if(socket_in != -1) - { - tcpsock_set_tcp_nodelay(socket_in); - tcpsock_set_timeout(socket_in, 10); - misc_print(1,"Incomming connection\n"); - tcpsock_get_ip(socket_in, buf); - misc_print(1, "CONNECT --> %s\n", buf); - if(socket_out == -1) - { - if(TCPQuiet == 0) - misc_swrite_no_trans(fdSerial, "\r\nRING"); - if(MODEMSOUND) - set_pcm_volume(modemVolume); - play_ring_sound(buf); - play_connect_sound(buf); - if(TCPQuiet == 0) - misc_swrite_no_trans(fdSerial, "\r\nCONNECT %d\r\n", baudRate); - serial2_set_DCD(serialDevice, fdSerial, TRUE); - do - { - rdLen = read(socket_in, buf, sizeof(buf)); - if (rdLen > 0) - { - write(fdSerial, buf, rdLen); - show_debug_buf("TSERV IN", buf, rdLen); - } - else if (rdLen == 0) - { - if(socket_in != -1) - close(socket_in); - socket_in = -1; - misc_print(1, "tcplst_thread_function() --> Connection Closed.\n"); - } - } while (socket_in != -1); - if(TCPQuiet == 0) - misc_swrite_no_trans(fdSerial, "\r\nNO CARRIER\r\n"); - serial2_set_DCD(serialDevice, fdSerial, FALSE); - } - else - { - char busyStr[] = "\r\nBUSY"; - misc_print(1, "Sending BUSY message and disconnecting.,\n"); - tcpsock_write(socket_in, busyStr, strlen(busyStr)); - sleep(2); - tcpsock_close(socket_in); - socket_in = -1; - } - } - else - { - sleep(5); - } - - } while(TRUE); -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void * socket_thread_function(void * x) -// Thread function for TCP input -// -void * tcpsock_thread_function (void * x) -{ - unsigned char buf[100]; - int rdLen; - do - { - rdLen = tcpsock_read(socket_out, (char *) buf, sizeof(buf)); - if (rdLen > 0) - { - write(fdSerial, buf, rdLen); - show_debug_buf("TSOCK IN ", buf, rdLen); - } - else if(rdLen < 1) - misc_print(1, "ERROR: tcpsock_thread_function() --> rdLen < 1\n"); - } while (rdLen > 0 && socket_out != -1); - if(socket_out != -1) - close(socket_out); - socket_out = -1; - if(MIDI_DEBUG) - misc_print(1, "TCPSOCK Thread fuction exiting.\n", socket_out); - if(TCPQuiet == 0) - misc_swrite_no_trans(fdSerial, "\r\nNO CARRIER\r\n"); - pthread_exit(NULL); -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void * udpsock_thread_function(void * x) -// Thread function for UDP input -// -void * udpsock_thread_function (void * x) -{ - unsigned char buf[100]; - int rdLen; - do - { - rdLen = udpsock_read(socket_in, (char *) buf, sizeof(buf)); - if (rdLen > 0) - { - write(fdSerial, buf, rdLen); - show_debug_buf("USOCK IN ", buf, rdLen); - } - } while (TRUE); -} - - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void * udpsock_thread_function2(void * x) -// Thread function for UDP input for external device like Raspberry Pi -// -void * udpsock_thread_function2 (void * x) -{ - unsigned char buf[100]; - int rdLen; - do - { - rdLen = udpsock_read(socket_in, (char *) buf, sizeof(buf)); - if (rdLen > 0) - { - write_alsa_packet(buf, rdLen); - //show_debug_buf("USOCK2 IN", buf, rdLen); - } - } while (TRUE); -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void do_check_modem_hangup(char * buf, int bufLen) -// -// -void do_check_modem_hangup(int * socket, char * buf, int bufLen) -{ - static char lineBuf[6]; - static char iLineBuf = 0; - static int plusCount = 0; - static int NEEDSTOP = FALSE; - static struct timeval start; - static struct timeval stop; - char tmp[100] = ""; - - for (char * p = buf; bufLen-- > 0; p++) - { - switch(*p) - { - case '+': // RESET - gettimeofday(&start, NULL); - iLineBuf = 0; - lineBuf[iLineBuf] = 0x00; - plusCount++; - NEEDSTOP = TRUE; - break; - case 0x0D:// [RETURN] - if(plusCount >= 3 && iLineBuf >= 3 && memcmp(lineBuf, "ATH", 3) == 0) - { - int delay = misc_get_timeval_diff(&start, &stop); - if(TCPATHDelay == 0 || delay > TCPATHDelay) - { - tcpsock_close(*socket); - *socket = -1; - serial2_set_DCD(serialDevice, fdSerial, FALSE); - sprintf(tmp, "\r\nHANG-UP DETECTED\r\n"); - misc_print(1, "HANG-UP Detected --> %d\n", delay); - misc_swrite(fdSerial, tmp); - sleep(1); - if(TCPQuiet == 0) - misc_swrite_no_trans(fdSerial, "OK\r\n"); - } - else - misc_print(1, "HANG-UP Rejected --> %d\n", delay); - } - iLineBuf = 0; - lineBuf[iLineBuf] = 0x00; - plusCount = 0; - break; - default: - if (plusCount >= 3 && iLineBuf < sizeof(lineBuf)-1) - { - if (NEEDSTOP) - { - gettimeofday(&stop, NULL); - NEEDSTOP = FALSE; - } - lineBuf[iLineBuf++] = *p; - lineBuf[iLineBuf] = 0x00; - } - else - plusCount = 0; - break; - } - } -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void do_telnet_negotiate() -// -// -#define DO 0xfd -#define WONT 0xfc -#define WILL 0xfb -#define DONT 0xfe -#define CMD 0xff -#define CMD_ECHO 1 -#define CMD_WINDOW_SIZE 31 - -void do_telnet_negotiate() -{ - char buf[3]; - int rdLen1; - int rdLen2; - int rdLen3; - int wrLen = 0; - unsigned char msg1[] = {0xff, 0xfb, 0x1f}; - unsigned char msg2[] = {0xff, 0xfa, 0x1f, 0x00, 0x50, 0x00, 0x18, 0xff, 0xf0}; - - tcpsock_set_timeout(socket_out, 3); - misc_print(1, "Telnet negotiation --> START\n"); - do - { - rdLen1 = tcpsock_read(socket_out, &buf[0], 1); - if (rdLen1 == 1 && buf[0] == CMD) - { - //misc_print(1, "Telnet negotiation --> CMD\n"); - rdLen2 = tcpsock_read(socket_out, &buf[1], 1); - rdLen3 = tcpsock_read(socket_out, &buf[2], 1); - if (buf[1] == DO && buf[2] == CMD_WINDOW_SIZE) - { - //misc_print(1, "Telnet negotiation --> N1\n"); - wrLen = tcpsock_write(socket_out, msg1, sizeof(msg1)); - if (wrLen < 0) goto end; - wrLen = tcpsock_write(socket_out, msg2, sizeof(msg2)); - if (wrLen < 0) goto end; - } - else - { - //misc_print(1, "Telnet negotiation --> N2\n"); - for (int i = 0; i < sizeof(buf); i++) - { - if (buf[i] == DO) - buf[i] = WONT; - else if (buf[i] == WILL) - buf[i] = DO; - } - wrLen = tcpsock_write(socket_out, buf, sizeof(buf)); - if (wrLen < 0) goto end; - buf[0] = CMD; - } - } - else if(rdLen1 = 1) - write(fdSerial, &buf[0], 1); - } while (buf[0] == CMD && rdLen1 == 1); -end: - misc_print(1, "Telnet negotiation --> END\n"); - if (wrLen < 0) - { - misc_print(0, "ERROR: Telnet negotiation failed\n"); - } - tcpsock_set_timeout(socket_out, 0); -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// BOOL do_file_picker(char * pathBuf, char * resultBuf) -// -// -int do_file_picker(char * pathBuf, char * fileNameBuf) -{ - int DIR = 0; - int result; - char * endPtr; - do - { - result = misc_list_files(pathBuf, fdSerial, TCPTermRows, fileNameBuf, &DIR); - if(result) - if (DIR) - { - if(strcmp(fileNameBuf, ".") == 0 || - strcmp(fileNameBuf, "..") == 0) - { - endPtr = strrchr(pathBuf, '/'); - if (endPtr != NULL && strlen(pathBuf) > 1) - *endPtr = (char) 0x00; - } - else - { - strcat(pathBuf, "/"); - strcat(pathBuf, fileNameBuf); - } - } - else - { - misc_swrite(fdSerial, "Selected file --> "); - misc_swrite(fdSerial, fileNameBuf); - } - } while (result && DIR); - return result; -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// int get_softsynth_port(int softSynth) -// -// -int get_softsynth_port(int softSynth) -{ - int midiPort = alsa_get_midi_port("MT-32"); - if (midiPort == -1) - midiPort = alsa_get_midi_port("FLUID Synth"); - if (midiPort == -1) - { - switch(softSynth) - { - case MUNT: - midiPort = start_munt(); - break; - case FluidSynth: - midiPort = start_fsynth(); - break; - case -1: //do nothing. - break; - } - } - return midiPort; -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// BOOL handle_at_command(char * lineBuf) -// -// -#define KILL_MP3_SLEEP if(MP3){killall_mpg123(1);MP3 = FALSE;} -int handle_at_command(char * lineBuf) -{ - static int TELNET_NEGOTIATE = TRUE; - static int MP3 = FALSE; - char tmp[1024] = ""; - char * endPtr; - char fileName [256]; - char audioError[] = "\r\nBad news, you have no audio device :( --> %s"; - - if(memcmp(lineBuf, "ATDT", 4) == 0) - { - char * ipAddr = &lineBuf[4]; - if(ipAddr[0] != (char) 0x00) - directory_search(midiLinkDIR, ipAddr, ipAddr); - char * prtSep = strchr(ipAddr, ':'); - if(prtSep == NULL) - prtSep = strchr(ipAddr, '*'); // with NCOMM? - char * port = (prtSep == NULL)?NULL:(prtSep + 1); - if (prtSep != NULL) *prtSep = 0x00; - if (strlen(ipAddr) < 3) - misc_show_atdt(fdSerial); - else - { - int ipError = FALSE; - int iPort = (port == NULL)?23:strtol(port, &endPtr, 10); - if (!misc_is_ip_addr(ipAddr)) - { - char domainName[30]; - getdomainname(domainName, sizeof(domainName)); - if(strcmp(domainName, "(none)") != 0 && misc_count_str_chr(ipAddr, '.') < 1) - { - strcat(ipAddr, "."); - strcat(ipAddr, domainName); - misc_print(1, "Doing domain name fix --> %s\n", ipAddr); - } - if(!misc_hostname_to_ip(ipAddr, ipAddr)) - { - misc_swrite(fdSerial, "\r\nERROR: Unable to convert hostname '%s' to IP address.", ipAddr); - ipError = TRUE; - } - } - if(!ipError) - { - misc_swrite(fdSerial, "\r\nDIALING %s:%d\r\n", ipAddr, iPort); - serial_do_tcdrain(fdSerial); - if(MODEMSOUND) - set_pcm_volume(modemVolume); - play_dial_sound(tmp, ipAddr); - serial_do_tcdrain(fdSerial); - if(MODEMSOUND) - sleep(1); - serial2_set_DCD(serialDevice, fdSerial, FALSE); - socket_out = tcpsock_client_connect(ipAddr, iPort, fdSerial); - } - if(socket_out > 0) - { - if (TELNET_NEGOTIATE) - do_telnet_negotiate(); - play_ring_sound(tmp); - play_connect_sound(tmp); - if(TCPQuiet == 0) - misc_swrite_no_trans(fdSerial, "\r\nCONNECT %d\r\n", baudRate); - serial_do_tcdrain(fdSerial); - sleep(1); - int status = pthread_create(&socketInThread, NULL, tcpsock_thread_function, NULL); - serial2_set_DCD(serialDevice, fdSerial, TRUE); - return TRUE; - } - } - } - else if (memcmp(lineBuf, "ATBAUD", 6) == 0) - { - char * baud = &lineBuf[6]; - int iBaud = strtol(baud, &endPtr, 10); - int iTemp = serial2_baud_at_index(iBaud); - iBaud = (misc_is_number(baud) && iTemp > 0)?iTemp:iBaud; - if (serial2_is_valid_rate (iBaud)) - { - int sec = 10; - misc_swrite(fdSerial, "\r\nSetting BAUD to %d in %d seconds...", iBaud, sec); - sleep(sec); - serial2_set_baud(serialDevice, fdSerial, iBaud); - baudRate = iBaud; - misc_swrite(fdSerial, "\r\nBAUD has been set to %d", iBaud); - } - else - { - if(baud[0] != 0x00) - misc_swrite(fdSerial, "\r\nBAUD rate '%s' is not valid.", baud); - serial2_show_menu(fdSerial); - } - } - else if (memcmp(lineBuf, "ATIP", 4) == 0) - { - misc_show_atip(fdSerial); - } - else if (memcmp(lineBuf, "ATK", 3) == 0) - { - char * hayesMode = &lineBuf[3]; - if(misc_is_number(hayesMode)) - { - int iHayesMode = strtol(hayesMode, &endPtr, 10); - serial_set_flow_control(fdSerial, iHayesMode); - } - else - serial_set_flow_control(fdSerial, -1); - } - else if (memcmp(lineBuf, "ATTEL", 5) == 0) - { - if (lineBuf[5] == '0') - TELNET_NEGOTIATE = FALSE; - else if (lineBuf[5] == '1') - TELNET_NEGOTIATE = TRUE; - misc_swrite(fdSerial, "\r\nTelnet Negotiations --> %s", TELNET_NEGOTIATE?"TRUE":"FALSE"); - } - else if (memcmp(lineBuf, "ATMP3", 5) == 0) - { - if (misc_check_device(PCMDevice)) - { - if(lineBuf[5] == '!') - { - misc_swrite(fdSerial, "\r\nMP3 --> OFF"); - killall_mpg123(0); - } - else if(do_file_picker(MP3Path, fileName)) - { - chdir("/root"); - set_pcm_volume(MP3Volume); - sprintf(tmp, "taskset %d mpg123 -o alsa \"%s/%s\" 2> /tmp/mpg123 & ", CPUMASK, MP3Path, fileName); - if(!MP3) - { - killall_aplaymidi(0); - killall_softsynth(3); - } - killall_mpg123(0); - misc_print(1, "Play MP3 --> %s\n", tmp); - system(tmp); - misc_swrite(fdSerial, "\r\n"); - sleep(1); - misc_file_to_serial(fdSerial, "/tmp/mpg123", TCPTermRows); - MP3 = TRUE; - } - } - else - misc_swrite(fdSerial, audioError, PCMDevice); - } - else if (memcmp(lineBuf, "ATMID", 5) == 0) - { - if (misc_check_device(PCMDevice)) - { - if(lineBuf[5] == '!') - { - killall_aplaymidi(0); - misc_swrite(fdSerial, "\r\nMIDI --> OFF"); - sleep(2); - int midiPort = get_softsynth_port(-1); - if(midiPort != -1) - { - alsa_open_seq(midiPort, 0); - alsa_send_midi_raw(all_notes_off, sizeof(all_notes_off)); - alsa_close_seq(); - } - } - else if(lineBuf[5] == '1') - { - KILL_MP3_SLEEP; - killall_aplaymidi(0); - misc_swrite(fdSerial, "\r\nLoading --> FluidSynth"); - killall_softsynth(3); - TCPSoftSynth = FluidSynth; - get_softsynth_port(TCPSoftSynth); - } - else if(lineBuf[5] == '2') - { - KILL_MP3_SLEEP; - killall_aplaymidi(0); - misc_swrite(fdSerial, "\r\nLoading --> MUNT"); - killall_softsynth(3); - TCPSoftSynth = MUNT; - get_softsynth_port(TCPSoftSynth); - } - else if(lineBuf[5] == 'S' && lineBuf[6] == 'F') - { - strcpy(tmp, fsynthSoundFont); - char * dir = strrchr(tmp, '/'); - if(dir != NULL) - *dir = (char) 0x00; - else - tmp[0] = (char) 0x00; - if (do_file_picker(tmp, fileName)) - { - strcpy(fsynthSoundFont, tmp); - strcat(fsynthSoundFont, "/"); - strcat(fsynthSoundFont, fileName); - misc_swrite(fdSerial, "\r\n SoundFont -->"); - misc_swrite(fdSerial, fsynthSoundFont); - sprintf(tmp,"sed -i '{s|^FSYNTH_SOUNDFONT[[:space:]]*=.*|FSYNTH_SOUNDFONT = %s|}' %s", - fsynthSoundFont, midiLinkINI); - system(tmp); - } - } - else if(do_file_picker(MIDIPath, fileName)) - { - KILL_MP3_SLEEP; - killall_aplaymidi(0); - int midiPort = get_softsynth_port(TCPSoftSynth); - chdir("/root"); - sprintf(tmp, "taskset %d aplaymidi --port %d \"%s/%s\" 2> /tmp/aplaymidi & ", CPUMASK, midiPort, MIDIPath, fileName);; - misc_print(1, "Play MIDI --> %s\n", tmp); - system(tmp); - misc_swrite(fdSerial, "\r\n"); - sleep(1); - misc_file_to_serial(fdSerial, "/tmp/aplaymidi", 0); - MP3 = FALSE; - } - } - else - { - misc_swrite(fdSerial, audioError, PCMDevice); - } - } - else if (memcmp(lineBuf, "ATSZ", 4) == 0) - { - if(do_file_picker(downloadPath, fileName)) - { - sprintf(tmp, "%s/%s", downloadPath, fileName); - misc_print(1, "Zmodem download --> %s\n", tmp); - serial_do_tcdrain(fdSerial); - misc_do_pipe(fdSerial, "/bin/sz","sz", tmp, NULL, NULL, NULL, NULL); - sleep(3); - } - } - else if (memcmp(lineBuf, "ATRZ", 4) == 0) - { - if(chdir (uploadPath) == 0) - { - misc_swrite(fdSerial, "\r\nUpload path --> '%s'", uploadPath); - misc_swrite(fdSerial, "\r\nUpload file using Zmodem protocol now...\r\n"); - serial_do_tcdrain(fdSerial); - misc_do_pipe(fdSerial, "/bin/rz", "rz", NULL, NULL, NULL, NULL, NULL); - chdir("/root"); - sleep(3); - } - else - misc_swrite(fdSerial, "\r\nERROR: Upload path invalid --> '%s'", uploadPath); - } - else if (memcmp(lineBuf, "ATROWS", 6) == 0) - { - char * strRows = &lineBuf[6]; - if(!misc_is_number(strRows)) - { - for (int i = 50; i > 1; i--) - misc_swrite(fdSerial, "\r\n%2d", i); - } - else - { - TCPTermRows = strtol(strRows, &endPtr, 10); - misc_swrite(fdSerial, "\r\nROWS --> %d", TCPTermRows); - serial_do_tcdrain(fdSerial); - } - } - else if (memcmp(lineBuf, "ATINI", 5) == 0) - { - misc_file_to_serial(fdSerial, midiLinkINI, TCPTermRows); - } - else if (memcmp(lineBuf, "ATDIR", 5) == 0) - { - misc_file_to_serial(fdSerial, midiLinkDIR, TCPTermRows); - } - else if (memcmp(lineBuf, "ATTRANS", 7) == 0) - { - char * validOptions = "\r\nValid options --> 0, 1, 2" - "\r\n-------------------------" - "\r\n0 = NONE" - "\r\n1 = PETSKII (C64)" - "\r\n2 = ATASCII (Atari 8-bit)" - "\r\n-------------------------"; - if(misc_is_number(&lineBuf[7])) - { - int tmpMode = strtol(&lineBuf[7], &endPtr, 10); - if (tmpMode >= 0 && tmpMode <= 2) - TCPAsciiTrans = tmpMode; - else - misc_swrite(fdSerial, validOptions); - } - else if(lineBuf[7] != (char) 0x00) - misc_swrite(fdSerial, validOptions); - misc_swrite(fdSerial, "\r\nASCII translation --> %s", - misc_trans_to_str(TCPAsciiTrans)); - } - else if (memcmp(lineBuf, "ATM", 3) == 0) - { - char * pct = strchr(&lineBuf[3], '%'); - if (pct != NULL) - *pct = (char) 0x00; - if(misc_is_number(&lineBuf[3])) - { - int tmpVol = strtol(&lineBuf[3], &endPtr, 10); - if(pct) - { - if(tmpVol <= 100) - { - modemVolume = tmpVol; - } - else - { - misc_swrite(fdSerial,"\r\nValid options --> 0-100%%"); - } - } - else - switch(tmpVol) - { - case 0: - MODEMSOUND = FALSE; - break; - case 1: - MODEMSOUND = TRUE; - break; - default: - misc_swrite(fdSerial,"\r\nUnsupported option --> '%s'", &lineBuf[3]); - break; - } - } - else - { - if(lineBuf[3] != (char) 0x00) - { - misc_swrite(fdSerial,"\r\nUnsupported option --> '%s'", &lineBuf[3]); - } - } - if(modemVolume != -1 && MODEMSOUND) - misc_swrite(fdSerial, "\r\nModem sounds = %s : volume = %d%%", MODEMSOUND?"ON":"OFF", modemVolume); - else - misc_swrite(fdSerial,"\r\nModem sounds = %s", MODEMSOUND?"ON":"OFF"); - } - else if (memcmp(lineBuf, "ATVER", 5) == 0) - { - misc_swrite(fdSerial, "\r\n"); - misc_swrite(fdSerial, helloStr); - } - else if (memcmp(lineBuf, "ATHELP", 6) == 0) - { - misc_show_at_commands(fdSerial, TCPTermRows); - } - else if (memcmp(lineBuf, "ATUARTTEST", 6) == 0) - { - if (lineBuf[10] == '!') - TCPTermRows = 0; - while (TRUE) - { - misc_show_at_commands(fdSerial, TCPTermRows); - misc_file_to_serial(fdSerial, midiLinkDIR, TCPTermRows); - } - } - else if (memcmp(lineBuf, "ATD", 3) == 0) - { - switch(lineBuf[3]) - { - case '0' : - case '1' : - TCPDTR = 1; - misc_print(1, "Setting DTR mode --> Normal\n"); - break; - case '2' : - TCPDTR = 2; - misc_print(1, "Setting DTR mode --> Hangup\n"); - break; - default: - misc_swrite(fdSerial,"\r\nUnsupported DTR option --> '%s'", &lineBuf[3]); - break; - } - } - else if (memcmp(lineBuf, "ATQ", 3) == 0) - { - switch(lineBuf[3]) - { - case '0' : - TCPQuiet = 0; - misc_print(1, "Setting result code mode --> Verbose\n"); - break; - case '1' : - TCPQuiet = 1; - misc_print(1, "Setting result code mode --> Quiet\n"); - break; - default: - misc_swrite(fdSerial,"\r\nUnsupported result code mode --> '%s'", &lineBuf[3]); - break; - } - } - else if (memcmp(lineBuf, "ATZ", 3) == 0) - { - misc_print(1, "Resetting TCP defaults...\n"); - MODEMSOUND = DEFAULT_MODEMSOUND; - modemVolume = DEFAULT_modemVolume; - TCPAsciiTrans = DEFAULT_TCPAsciiTrans; - TCPTermRows = DEFAULT_TCPTermRows; - TCPFlow = DEFAULT_TCPFlow; - TCPDTR = DEFAULT_TCPDTR; - TCPQuiet = DEFAULT_TCPQuiet; - TELNET_NEGOTIATE = TRUE; - misc_print(1, "Reloading INI defaults...\n"); - misc_get_core_name(tmp, sizeof(tmp)); - if(misc_check_file(midiLinkINI)) - ini_read_ini(midiLinkINI, tmp, 1); - } - else if (memcmp(lineBuf, "AT", 2) == 0) - { - if (lineBuf[2] != (char) 0x00) - { - misc_swrite(fdSerial, "\r\nUnknown Command '%s'", &lineBuf[2]); - misc_print(1, "ERROR : Unknown AT command --> '%s'\n", &lineBuf[2]); - } - } - - return FALSE; -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void do_modem_emulation(char * buf, int bufLen) -// -// -void do_modem_emulation(char * buf, int bufLen) -{ - static char lineBuf[150] = ""; - static char iLineBuf = 0; - char * lbp; - - show_debug_buf("SER OUT ", buf, bufLen); - for (char * p = buf; bufLen-- > 0; p++) - { - *p = toupper(*p); - switch(*p) - { - case ' ': // [SPACE] no space - break; - case 0x08: // [DELETE] - case 0x14: // [PETSKII DELETE] - case 0xf8: // [BACKSPACE] - if (iLineBuf > 0) - { - lineBuf[--iLineBuf] = 0x00; - write(fdSerial, p, 1); - } - break; - case 0x0D: // [RETURN] - lbp = lineBuf; - int CONNECT = FALSE; - if(iLineBuf > 1 && lineBuf[0] == 'A' && lineBuf[1] == 'T') - { - while (lbp && !CONNECT) - { - char * amp = strchr(lbp, '&'); - if(amp) - *amp = 0x00; - CONNECT = handle_at_command(lbp); - if(amp) - { - lbp = amp-1; - lbp[0] = 'A'; - lbp[1] = 'T'; - } - else - lbp = NULL; - } - if (!CONNECT && TCPQuiet == 0) - misc_swrite_no_trans(fdSerial, "\r\nOK\r\n"); - } - else - misc_swrite(fdSerial, "\r\n"); - iLineBuf = 0; - lineBuf[iLineBuf] = 0x00; - break; - default: - if (iLineBuf < 80) - { - lineBuf[iLineBuf++] = *p; - write(fdSerial, p, 1); - lineBuf[iLineBuf] = 0x00; - } - } - } -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void * midi_thread_function(void * x) -// Thread function for /dev/midi input -// -void * midi_thread_function (void * x) -{ - unsigned char buf [100]; - int rdLen; - do - { - rdLen = read(fdMidi, &buf, sizeof(buf)); - if (rdLen > 0) - { - write(fdSerial, buf, rdLen); - show_debug_buf("MIDI IN ", buf, rdLen); - } - else - { - misc_print(1, "ERROR: midi_thread_function() reading %s --> %d : %s \n", midiDevice, rdLen, strerror(errno)); - } - } while (TRUE); -} - -/////////////////////////////////////////////////////////////////////////////////////// -// -// void * serial_thread_function(void * x) -// Thread function for /dev/ttyUSB input -// -void * serial_thread_function (void * x) -{ - unsigned char buf [100]; - int rdLen; - do - { - rdLen = read(fdSerialUSB, &buf, sizeof(buf)); - if (rdLen > 0) - { - write(fdSerial, buf, rdLen); - show_debug_buf("SERIAL IN ", buf, rdLen); - } - else - { - misc_print(1, "ERROR: serial_thread_function() reading %s --> %d : %s \n", serialDeviceUSB, rdLen, strerror(errno)); - } - } while (TRUE); -} - /////////////////////////////////////////////////////////////////////////////////////// // // write_midi_packet() @@ -1251,14 +246,92 @@ void write_midi_packet(char * buf, int bufLen) } } + /////////////////////////////////////////////////////////////////////////////////////// // -// test_midi_device() -// Play a test note : this is for /dev/midi +// write_socket_packet() +// this is for TCP/IP // -void test_midi_device() +void write_socket_packet(int sock, char * buf, int bufLen) { - write_midi_packet(test_note, sizeof(test_note)); + if (mode == ModeTCP) + { + if(tcpsock_write(sock, buf, bufLen) < 1) + { + close(socket_out); + //tcpsock_close(socket_out); + socket_out = -1; + } + } + else + udpsock_write(sock, buf, bufLen); + + show_debug_buf("SOCK OUT ", buf, bufLen); +} + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void * udpsock_thread_function(void * x) +// Thread function for UDP input +// +void * udpsock_thread_function (void * x) +{ + unsigned char buf[100]; + int rdLen; + do + { + rdLen = udpsock_read(socket_in, (char *) buf, sizeof(buf)); + if (rdLen > 0) + { + write(fdSerial, buf, rdLen); + show_debug_buf("USOCK IN ", buf, rdLen); + } + } while (TRUE); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void * udpsock_thread_function_ext(void * x) +// Thread function for UDP input for external device like Raspberry Pi +// +void * udpsock_thread_function_ext (void * x) +{ + unsigned char buf[100]; + int rdLen; + do + { + rdLen = udpsock_read(socket_in, (char *) buf, sizeof(buf)); + if (rdLen > 0) + { + write_alsa_packet(buf, rdLen); + //show_debug_buf("USOCK2 IN", buf, rdLen); + } + } while (TRUE); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void * midi_thread_function(void * x) +// Thread function for /dev/midi input +// +void * midi_thread_function (void * x) +{ + unsigned char buf [100]; + int rdLen; + do + { + rdLen = read(fdMidi, &buf, sizeof(buf)); + if (rdLen > 0) + { + write(fdSerial, buf, rdLen); + show_debug_buf("MIDI IN ", buf, rdLen); + } + else + { + misc_print(1, "ERROR: midi_thread_function() reading %s --> %d : %s \n", midiDevice, rdLen, strerror(errno)); + } + } while (TRUE); } /////////////////////////////////////////////////////////////////////////////////////// @@ -1294,24 +367,63 @@ void * midiINin_thread_function (void * x) /////////////////////////////////////////////////////////////////////////////////////// // -// write_socket_packet() -// this is for TCP/IP +// void * serial_thread_function(void * x) +// Thread function for /dev/ttyUSB input // -void write_socket_packet(int sock, char * buf, int bufLen) +void * serial_thread_function (void * x) { - if (mode == ModeTCP) + unsigned char buf [100]; + int rdLen; + do { - if(tcpsock_write(sock, buf, bufLen) < 1) + rdLen = read(fdSerialUSB, &buf, sizeof(buf)); + if (rdLen > 0) { - close(socket_out); - //tcpsock_close(socket_out); - socket_out = -1; + write(fdSerial, buf, rdLen); + show_debug_buf("SERIAL IN ", buf, rdLen); + } + else + { + misc_print(1, "ERROR: serial_thread_function() reading %s --> %d : %s \n", serialDeviceUSB, rdLen, strerror(errno)); + } + } while (TRUE); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// int get_softsynth_port(int softSynth) +// +// +int get_softsynth_port(int softSynth) +{ + int midiPort = alsa_get_midi_port("MT-32"); + if (midiPort == -1) + midiPort = alsa_get_midi_port("FLUID Synth"); + if (midiPort == -1) + { + switch(softSynth) + { + case MUNT: + midiPort = start_munt(); + break; + case FluidSynth: + midiPort = start_fsynth(); + break; + case -1: //do nothing. + break; } } - else - udpsock_write(sock, buf, bufLen); + return midiPort; +} - show_debug_buf("SOCK OUT ", buf, bufLen); +/////////////////////////////////////////////////////////////////////////////////////// +// +// test_midi_device() +// Play a test note : this is for /dev/midi +// +void test_midi_device() +{ + write_midi_packet(test_note, sizeof(test_note)); } /////////////////////////////////////////////////////////////////////////////////////// @@ -1352,8 +464,9 @@ void close_fd() void signal_handler(int signum, siginfo_t *info, void *ptr) { //write(STDERR_FILENO, SIGTERM_MSG, sizeof(SIGTERM_MSG)); - killall_mpg123(0); - killall_aplaymidi(0); + modem_killall_mpg123(0); + modem_killall_aplaymidi(0); + modem_killall_aplay(0); killall_softsynth(0); close_fd(); } @@ -1443,8 +556,8 @@ int main(int argc, char *argv[]) if(misc_check_args_option(argc, argv, "UDPFSYNTH")) mode = ModeUDPFSYNTH; } - killall_mpg123(0); - killall_aplaymidi(0); + modem_killall_mpg123(0); + modem_killall_aplaymidi(0); killall_softsynth(3); if (mode == ModeMUNT || mode == ModeMUNTGM || mode == ModeFSYNTH || @@ -1597,7 +710,7 @@ int main(int argc, char *argv[]) if(socket_in > 0) { misc_print(0, "Socket Listener created on port %d.\n", UDPServerPort); - status = pthread_create(&socketInThread, NULL, udpsock_thread_function2, NULL); + status = pthread_create(&socketInThread, NULL, udpsock_thread_function_ext, NULL); if (status == -1) { misc_print(0, "ERROR: unable to create UDP socket input thread.\n"); @@ -1675,7 +788,7 @@ int main(int argc, char *argv[]) socket_lst = tcpsock_server_open(TCPServerPort); if(socket_lst != -1) { - status = pthread_create(&socketLstThread, NULL, tcplst_thread_function, NULL); + status = pthread_create(&socketLstThread, NULL, modem_tcplst_thread_function, NULL); if (status == -1) { misc_print(0, "ERROR: unable to create socket listener thread.\n"); @@ -1796,7 +909,7 @@ int main(int argc, char *argv[]) { case ModeUSBMIDI: misc_print(1, "Sending MIDI --> all-notes-off\n"); - write_midi_packet(all_notes_off, sizeof(all_notes_off)); + write_midi_packet(all_notes_off, sizeof_all_notes_off); if(strlen(MT32LCDMsg) > 0) { misc_print(1, "Sending MT-32 LCD --> '%s'\n", MT32LCDMsg); @@ -1842,7 +955,7 @@ int main(int argc, char *argv[]) if (socket_out != -1 && baudRate == 31250) { misc_print(1, "Sending UDP --> all-notes-off\n"); - write_socket_packet(socket_out, all_notes_off, sizeof(all_notes_off)); + write_socket_packet(socket_out, all_notes_off, sizeof_all_notes_off); if(strlen(MT32LCDMsg) > 0) { misc_print(1, "Sending UDP MT-32 LCD --> '%s'\n", MT32LCDMsg); @@ -1873,18 +986,18 @@ int main(int argc, char *argv[]) { if(socket_in != -1) { - do_check_modem_hangup(&socket_in, buf, rdLen); + modem_do_check_hangup(&socket_in, buf, rdLen); if(socket_in != -1) write_socket_packet(socket_in, buf, rdLen); } if(socket_out != -1) { - do_check_modem_hangup(&socket_out, buf, rdLen); + modem_do_check_hangup(&socket_out, buf, rdLen); if(socket_out != -1) write_socket_packet(socket_out, buf, rdLen); } else if (socket_in == -1) - do_modem_emulation(buf, rdLen); + modem_do_emulation(buf, rdLen); } else if (TCPDTR == 2 && rdLen == 0 && serial2_get_DSR(fdSerial) == FALSE) @@ -1907,4 +1020,4 @@ int main(int argc, char *argv[]) } close_fd(); return 0; -} \ No newline at end of file +} diff --git a/midilink b/midilink index aad37f630922f9962ee8a79748f8be0827b9dab8..7b6cf0cac8661ad62e4d00503809aa8c041e6e1d 100755 GIT binary patch delta 26602 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 CW+veP delta 27634 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⁡3KG)1+zed&>(Qqn*&txX;*5FN8Z|Phz}8_L&a*m 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 diff --git a/misc.c b/misc.c index 601c8b9..46a752f 100644 --- a/misc.c +++ b/misc.c @@ -993,3 +993,5 @@ char * misc_hayes_ATQ_to_str(int dtr) } } + + diff --git a/mlinkutil b/mlinkutil index 244e02f271b6680d2395a5ad43fff5c0134d7b0f..317b421ea683ef06ceca64142f7f0a813e9cf684 100755 GIT binary patch delta 5375 zcmZ`-4Rlk-m7aO}@eekZY}v@zv7h8W8UM*Zf*>@=l029m987UoNJ(pg!x{oa5Vj#P zTbYHVv?0VWDNC9^XtSwI@kWmA#10sH8%|HQrCF1bE?J=K-O^*pl5Oz-y|-ZN?VTr? zz}c>kuQW6F&i(o3zB~7+?J$-O<91_dbvi~15kht+e5jFTA>vUIvY;F^7h#0l4NDPf zSc*}@k(y1K4KUeoq$5Xrl|#s-I`Z2;8p;#=;+8*_ef-PMPHxWm{axwDN2)J$_uxw$ z_ILkSosV&tc4*$ix4KtoMU~!P!y!WkLSgn@gwU<-)yX$8e!06gWt#IGa#RGuHA4dC zI&Yuct|7At3OC&~cDZs-JBEZ;+Z>Urj%vdA7T~P83vPz{+g!Q-SeJW= zEFPF-F1<>VbOj#Ceq2I2#*@X731o2(J0j|amvU=Z0I+76P{IDf)ebhtHrV~xB>Ox+ zv6yg#*=kp;yCxb(lFg0|7M?7ki3EbfIS}Nxh9N{ljlk(4MD>uM!+Z<#6rW-1I{%3Md+|Xu-@S8C!C8*fe~9>5BcMFP5&66Ul&jdT zuR+HbyW@ zu!*0E_%|T2uHhRs?~XGBT=x*rO?koW;OuRnoh`fKFa`*Ui;Ux6lXd$SCt?)TA?uhi%sEC;$#U&K>O3#HcdTmq|XDbq4 zfW!qyObU#0GjeZ$9EV;63%iwQWzh@TlaYJ$#)NK(Gu-w(wWSxQ9Uqs)rzc)4$EYR^ zqi@me={Xty7L~`^E8rO5-zE5UK=iHjd`)aLm|lt{8c2W0g9`Wi*vTez^5h9g{j4nd zLoVS~20M^yGLINr@K0s&(l`%@y&F>b2K;Wj%I98=250xU1YVKz-DwNklX6;gz59Kr z%s7a7UzbZjy!JF`0R+z}8qI(lr_#I$8sCt`Q{zlxC{GdCr6|_L72A5%dm6hmU~o45 zMMhbM6jHUUkxH!ZIE;e@Zp}qm9Hal0k^RJfhsp@o@*Oznx$X=JuR`S_WLxu2i<0WH z_(*77mm{+6-n=q|EGdH=k9gYv210|J<()xCq)8&((}3Za5mq(xRo zq-`f3kMF1qvU)b-uq?hRW1YE_uyZ;aL>37YP0=-()t32yYRwaIwBWQA;Y{f$P-o_5S-ot>dDISd^>3GcSO89*&{EDWl_v$`E&!0 z$cde?XGs=KQ50W0uv6E|kz+o!f+ON(F(YasTZsxr#SiF7_$zv*r)Tu|6g|wM8ZHXh z9G;fN%|SE4%8U;t_G4Z*04We;WxyJAM22b2oE+RhpPEye`O4UcEzNRh5b>B-`K*wR zj>r^!YfjEwOVHY7knl1K$tI{QauM?y;5&sx6&;>q#>w>NoD%a(K{Z*|rP^X_v2`)^ zTgNbA=pGwmx$P7Tm@YNuu3sDE3N*w-(#$QSQ_@*wQ5f4w>LQtXCcI-TLD}?V&?zir z1z45euQ?9qJ*FZ~!G_InY%xfSgNRS^g$HcCPQitpLL==mW_$MHnPmKzX{T`F-t1GD zeSUA&JUjYTvGkXYR1-A>BKdv&^9b|^8`;bpP!nXiagm+l zM1ZCGOn}8xJV^x$?g7Rd0iKsV2I+u@EE?guk1?IXI(lVpwx?0A0o=U0V{j2*<6n*+&~RpBL=^9H|WGh`VZ*1Dl;0q=V- z8yT+6#U06J=EmE8tx}v02b@ArJY=?hwlKiHi~%dj^0^0i0$2PavbZ-scM6v>oWcnG z$DE4V<^Z-Dhh=dW9Eq))<>@~MSjVL8P{Y{m=knB7Avhto9*V<$Z3nA`^8@2>%_axf zBXYs-6zXVF?nA3z30#v5?KAhB5qQP`CUqdc%^&0Vm79%mlH;83`I*J70hbWI$D8tF zC8!4aLaxb^9!Ttmz9JxteST)|C9szZ6_mls%3s0V$6kSs*Ej(P44B8 z5f9AXe_;0Z1G5tk%#J@WJN>}Sci#*R!WE@=3VqNUb5K&w()_%fxl-{-mWNsm$t9F% z=ugNoe;R_~x0E*K@t*UhClx$1y^p#xj{QE6xmka#Q)+1Hlr}WUV!7V}A$~NevPQ*S zlTd?9^Au?{Noj|6%x{kjZ<-G$#==bNh>Am#z8ffUNN9&fehTtTaYY4OE}JCLq!w^W zakzot(v(3x$!2)^?)2S2X(+sZA0%eM85lElaB#bvLWpMMd(yHdp?x-|-gp#BU{pLx zPv$?2Z_$6xFG^(wFvpW@rkW=475bGECu+ruX~&~t-9!p}y6AGg2pi~j-h^A|>wJCo zfeBU10x8Fuqe=QI-5V7TP9(Nx!uJ7sn_q#i(`BZT>~BxJBN>PIU#VO(c2&~i3sG?g z^_a@C*G;f}#Y~20GU;BGw5U8P=F-nhWq6oQn*J7lN&m*Y9=}by%%w@jN1cLokyF@4 z|7?DF#ZV2#Rkt-BIYOXoQSuvKG5Rw^FO3;+>#|v~_c2F&u1ER6~yu6)%O@_%Qv?!W`U2 z?-u5)Z&doQB5Q#guGa6P3K6bV3GT_J`H=n9>{OV>s5la}5NSYddGB6ls0y~J{vZvZ z18{M9tssTm%dsT|BJEoMEA+Xd%*DR=dQ+EE7-FmQK~6~|kQ_8ZXTzST87VYq?KMEB zWrxMGqWe7$A$`P8MHr3HBmtC^digPcTisimLjqIU_4g4l)zpf$0xN zONq|RPG0~IPJxvVu0_SQ_cUjL=0owK%o6X}>En=QMDZ|D@$g7##+(gf>O6dfCpz$c zzQPj%&PX3UQ(7?VDDakC!Nn}nKxsZ+Nt5Q8)}k@5BkxMGiCL~2<4FQEOHpwQE*^*r z_FmP`Tm(@OO@BQKD<-VG55^+iI&~fV_!8t}^LE%{sHSc640DWuZ=Cs5QTP%t>*%lM zm6cuhV_prdE*eB;Td$sIz`P|cZvzJoEAkB>zm4Luih>llLlcI%w+&rUaa%y$#K5FTm759qZ%Hq5<~339H0rioJmYXjJ|083P4=aCmb-i5IDS&MNn%W`8l zDu(?4R5g3ujfEE#`|th*!Zp-j-W*R#-`!YZWGPlJ!w&xgJ5Er!EMb;b;eHO>cB(Hg zDs2j=pg=yY+_?~$U#rAj5s;h-WY*&cp%^9C|IjrpO7#2XCe3fsG3ui)moMucE;rz# zl?|S%=4XCT_56A9$0DT2?%w_4 znreiqwm;LnWns0kWrHlnUQ_yDOIc8GBrCRmkzbau_qQCLmuc^4U>WE9$bNk6O`3 zcGTU9y6vc^74_IrZ!7AxqmNtB$9D8dEBeHa`dU$+9rd@OemnZK6@6+)XIjx2J38Bn z&f3vHD;lt)bFJu{9SydkLAzqdSc!G00gSqVzS~gq#G2ZtXi3fD)VjqGEl@j9Pxtnk z7gTsD{fjGKvlo6b*-MjZ3z81P=wLZQpVNnH^EJv3k#v1+fo2{2B)N|6tNk4wq4flQ kJ%OU>r#ndr_R}{>376@hzb3`T#~ldWfG}tNVyGhYzq#!~&;S4c delta 5186 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. +---------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "tcpsock.h" +#include "modem_snd.h" +#include "alsa.h" +#include "ini.h" +#include "serial.h" +#include "serial2.h" +#include "directory.h" + +#define DEFAULT_MODEMSOUND TRUE +#define DEFAULT_modemVolume -1 +#define DEFAULT_TCPAsciiTrans AsciiNoTrans +#define DEFAULT_TCPTermRows 22 +#define DEFAULT_TCPFlow -1 +#define DEFAULT_TCPDTR 1 +#define DEFAULT_TCPQuiet 0 + + +enum SOFTSYNTH TCPSoftSynth = FluidSynth; +enum ASCIITRANS TCPAsciiTrans = DEFAULT_TCPAsciiTrans; +char MP3Path[500] = "/media/fat/MP3"; +char MIDIPath[500] = "/media/fat/MIDI"; +char downloadPath[500] = "/media/fat"; +char uploadPath[100] = "/media/fat/UPLOAD"; +char modemConnectSndWAV[50] = ""; +char modemDialSndWAV[50] = ""; +char modemRingSndWAV[50] = ""; +int TCPATHDelay = 900; +int MP3Volume = -1; +unsigned int TCPTermRows = DEFAULT_TCPTermRows; +int modemVolume = DEFAULT_modemVolume; +int TCPQuiet = DEFAULT_TCPQuiet; +int MODEMSOUND = DEFAULT_MODEMSOUND; + +extern int CPUMASK; +extern int MODEMSOUND; +extern int fdSerial; +extern int socket_out; +extern int socket_lst; +extern int socket_in; +extern int MIDI_DEBUG; +extern int baudRate; +extern int TCPQuiet; +extern int modemVolume; +extern int sizeof_all_notes_off; +extern int TCPDTR; +extern int TCPFlow; +extern char all_notes_off[]; +extern char * midiLinkINI; +extern char * midiLinkDIR; +extern char * PCMDevice; +extern char * serialDevice; +extern char * helloStr; +extern char fsynthSoundFont[]; + +static pthread_t socketInThread; + +void show_debug_buf(char * descr, char * buf, int bufLen); +void killall_softsynth(int delay); +int get_softsynth_port(int softSynth); +void set_pcm_volume(int value); + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_killall_mpg123() +// +void modem_killall_mpg123(int delay) +{ + misc_print(0, "Killing --> mpg123\n"); + system("killall -q mpg123"); + if(delay) + sleep(delay); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_killall_aplaymidi() +// +void modem_killall_aplaymidi(int delay) +{ + misc_print(0, "Killing --> aplaymidi\n"); + system("killall -q aplaymidi"); + if(delay) + sleep(delay); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_killall_aplay() +// +void modem_killall_aplay(int delay) +{ + misc_print(0, "Killing --> aplay\n"); + system("killall -q aplay"); + if(delay) + sleep(delay); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_play_conenct_sound(char * tmp) +// +// +void modem_play_connect_sound(char * tmp) +{ + if (MODEMSOUND) + { + modem_killall_aplaymidi(0); + killall_softsynth(0); + modem_killall_mpg123(0); + modem_killall_aplay(0); + if(strlen(modemConnectSndWAV) > 0 && misc_check_file(modemConnectSndWAV)) + { + misc_print(1, "Playing WAV --> '%s'\n", modemConnectSndWAV); + sprintf(tmp, "aplay %s", modemConnectSndWAV); + system(tmp); + } + else + modem_snd("C"); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void play_conenct_sound(char * tmp) +// +// +void modem_play_ring_sound(char * tmp) +{ + if (MODEMSOUND) + { + modem_killall_aplaymidi(0); + killall_softsynth(0); + modem_killall_mpg123(0); + modem_killall_aplay(0); + if(strlen(modemRingSndWAV) > 0 && misc_check_file(modemRingSndWAV)) + { + misc_print(1, "Playing WAV --> '%s'\n", modemRingSndWAV); + sprintf(tmp, "aplay %s", modemRingSndWAV); + system(tmp); + } + else + modem_snd("R"); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_play_dial_sound(char * tmp, char * ipAddr) +// +// +void modem_play_dial_sound(char * tmp, char * ipAddr) +{ + if (MODEMSOUND) + { + modem_killall_aplaymidi(0); + killall_softsynth(0); + modem_killall_mpg123(0); + modem_killall_aplay(0); + if (strlen(modemDialSndWAV) > 0 && misc_check_file(modemDialSndWAV)) + { + misc_print(1, "Playing WAV --> '%s'\n", modemDialSndWAV); + sprintf(tmp, "aplay %s", modemDialSndWAV); + system(tmp); + } + else + modem_snd(ipAddr); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void * modem_socket_thread_function(void * x) +// Thread function for TCP input +// +void * modem_tcpsock_thread_function (void * x) +{ + unsigned char buf[100]; + int rdLen; + do + { + rdLen = tcpsock_read(socket_out, (char *) buf, sizeof(buf)); + if (rdLen > 0) + { + write(fdSerial, buf, rdLen); + show_debug_buf("TSOCK IN ", buf, rdLen); + } + else if(rdLen < 1) + misc_print(1, "ERROR: tcpsock_thread_function() --> rdLen < 1\n"); + } while (rdLen > 0 && socket_out != -1); + if(socket_out != -1) + close(socket_out); + socket_out = -1; + if(MIDI_DEBUG) + misc_print(1, "TCPSOCK Thread fuction exiting.\n", socket_out); + if(TCPQuiet == 0) + misc_swrite_no_trans(fdSerial, "\r\nNO CARRIER\r\n"); + pthread_exit(NULL); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void * modem_tcplst_thread_function(void * x) +// Thread function for TCP Listener input +// +void * modem_tcplst_thread_function (void * x) +{ + unsigned char buf[100]; + int rdLen; + + do + { + socket_in = tcpsock_accept(socket_lst); + if(socket_in != -1) + { + tcpsock_set_tcp_nodelay(socket_in); + tcpsock_set_timeout(socket_in, 10); + misc_print(1,"Incomming connection\n"); + tcpsock_get_ip(socket_in, buf); + misc_print(1, "CONNECT --> %s\n", buf); + if(socket_out == -1) + { + if(TCPQuiet == 0) + misc_swrite_no_trans(fdSerial, "\r\nRING"); + if(MODEMSOUND) + set_pcm_volume(modemVolume); + modem_play_ring_sound(buf); + modem_play_connect_sound(buf); + if(TCPQuiet == 0) + misc_swrite_no_trans(fdSerial, "\r\nCONNECT %d\r\n", baudRate); + serial2_set_DCD(serialDevice, fdSerial, TRUE); + do + { + rdLen = read(socket_in, buf, sizeof(buf)); + if (rdLen > 0) + { + write(fdSerial, buf, rdLen); + show_debug_buf("TSERV IN", buf, rdLen); + } + else if (rdLen == 0) + { + if(socket_in != -1) + close(socket_in); + socket_in = -1; + misc_print(1, "tcplst_thread_function() --> Connection Closed.\n"); + } + } while (socket_in != -1); + if(TCPQuiet == 0) + misc_swrite_no_trans(fdSerial, "\r\nNO CARRIER\r\n"); + serial2_set_DCD(serialDevice, fdSerial, FALSE); + } + else + { + char busyStr[] = "\r\nBUSY"; + misc_print(1, "Sending BUSY message and disconnecting.,\n"); + tcpsock_write(socket_in, busyStr, strlen(busyStr)); + sleep(2); + tcpsock_close(socket_in); + socket_in = -1; + } + } + else + { + sleep(5); + } + + } while(TRUE); +} + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_do_check_hangup(char * buf, int bufLen) +// +// +void modem_do_check_hangup(int * socket, char * buf, int bufLen) +{ + static char lineBuf[6]; + static char iLineBuf = 0; + static int plusCount = 0; + static int NEEDSTOP = FALSE; + static struct timeval start; + static struct timeval stop; + char tmp[100] = ""; + + for (char * p = buf; bufLen-- > 0; p++) + { + switch(*p) + { + case '+': // RESET + gettimeofday(&start, NULL); + iLineBuf = 0; + lineBuf[iLineBuf] = 0x00; + plusCount++; + NEEDSTOP = TRUE; + break; + case 0x0D:// [RETURN] + if(plusCount >= 3 && iLineBuf >= 3 && memcmp(lineBuf, "ATH", 3) == 0) + { + int delay = misc_get_timeval_diff(&start, &stop); + if(TCPATHDelay == 0 || delay > TCPATHDelay) + { + tcpsock_close(*socket); + *socket = -1; + serial2_set_DCD(serialDevice, fdSerial, FALSE); + sprintf(tmp, "\r\nHANG-UP DETECTED\r\n"); + misc_print(1, "HANG-UP Detected --> %d\n", delay); + misc_swrite(fdSerial, tmp); + sleep(1); + if(TCPQuiet == 0) + misc_swrite_no_trans(fdSerial, "OK\r\n"); + } + else + misc_print(1, "HANG-UP Rejected --> %d\n", delay); + } + iLineBuf = 0; + lineBuf[iLineBuf] = 0x00; + plusCount = 0; + break; + default: + if (plusCount >= 3 && iLineBuf < sizeof(lineBuf)-1) + { + if (NEEDSTOP) + { + gettimeofday(&stop, NULL); + NEEDSTOP = FALSE; + } + lineBuf[iLineBuf++] = *p; + lineBuf[iLineBuf] = 0x00; + } + else + plusCount = 0; + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void do_telnet_negotiate() +// +// +#define DO 0xfd +#define WONT 0xfc +#define WILL 0xfb +#define DONT 0xfe +#define CMD 0xff +#define CMD_ECHO 1 +#define CMD_WINDOW_SIZE 31 + +void modem_do_telnet_negotiate() +{ + char buf[3]; + int rdLen1; + int rdLen2; + int rdLen3; + int wrLen = 0; + unsigned char msg1[] = {0xff, 0xfb, 0x1f}; + unsigned char msg2[] = {0xff, 0xfa, 0x1f, 0x00, 0x50, 0x00, 0x18, 0xff, 0xf0}; + + tcpsock_set_timeout(socket_out, 3); + misc_print(1, "Telnet negotiation --> START\n"); + do + { + rdLen1 = tcpsock_read(socket_out, &buf[0], 1); + if (rdLen1 == 1 && buf[0] == CMD) + { + //misc_print(1, "Telnet negotiation --> CMD\n"); + rdLen2 = tcpsock_read(socket_out, &buf[1], 1); + rdLen3 = tcpsock_read(socket_out, &buf[2], 1); + if (buf[1] == DO && buf[2] == CMD_WINDOW_SIZE) + { + //misc_print(1, "Telnet negotiation --> N1\n"); + wrLen = tcpsock_write(socket_out, msg1, sizeof(msg1)); + if (wrLen < 0) goto end; + wrLen = tcpsock_write(socket_out, msg2, sizeof(msg2)); + if (wrLen < 0) goto end; + } + else + { + //misc_print(1, "Telnet negotiation --> N2\n"); + for (int i = 0; i < sizeof(buf); i++) + { + if (buf[i] == DO) + buf[i] = WONT; + else if (buf[i] == WILL) + buf[i] = DO; + } + wrLen = tcpsock_write(socket_out, buf, sizeof(buf)); + if (wrLen < 0) goto end; + buf[0] = CMD; + } + } + else if(rdLen1 = 1) + write(fdSerial, &buf[0], 1); + } while (buf[0] == CMD && rdLen1 == 1); +end: + misc_print(1, "Telnet negotiation --> END\n"); + if (wrLen < 0) + { + misc_print(0, "ERROR: Telnet negotiation failed\n"); + } + tcpsock_set_timeout(socket_out, 0); +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// BOOL do_file_picker(char * pathBuf, char * resultBuf) +// +// +int do_file_picker(char * pathBuf, char * fileNameBuf) +{ + int DIR = 0; + int result; + char * endPtr; + do + { + result = misc_list_files(pathBuf, fdSerial, TCPTermRows, fileNameBuf, &DIR); + if(result) + if (DIR) + { + if(strcmp(fileNameBuf, ".") == 0 || + strcmp(fileNameBuf, "..") == 0) + { + endPtr = strrchr(pathBuf, '/'); + if (endPtr != NULL && strlen(pathBuf) > 1) + *endPtr = (char) 0x00; + } + else + { + strcat(pathBuf, "/"); + strcat(pathBuf, fileNameBuf); + } + } + else + { + misc_swrite(fdSerial, "Selected file --> "); + misc_swrite(fdSerial, fileNameBuf); + } + } while (result && DIR); + return result; +} + + +/////////////////////////////////////////////////////////////////////////////////////// +// +// BOOL modem_handle_at_command(char * lineBuf) +// +// +#define KILL_MP3_SLEEP if(MP3){modem_killall_mpg123(1);MP3 = FALSE;} +int modem_handle_at_command(char * lineBuf) +{ + static int TELNET_NEGOTIATE = TRUE; + static int MP3 = FALSE; + char tmp[1024] = ""; + char * endPtr; + char fileName [256]; + char audioError[] = "\r\nBad news, you have no audio device :( --> %s"; + + if(memcmp(lineBuf, "ATDT", 4) == 0) + { + char * ipAddr = &lineBuf[4]; + if(ipAddr[0] != (char) 0x00) + directory_search(midiLinkDIR, ipAddr, ipAddr); + char * prtSep = strchr(ipAddr, ':'); + if(prtSep == NULL) + prtSep = strchr(ipAddr, '*'); // with NCOMM? + char * port = (prtSep == NULL)?NULL:(prtSep + 1); + if (prtSep != NULL) *prtSep = 0x00; + if (strlen(ipAddr) < 3) + misc_show_atdt(fdSerial); + else + { + int ipError = FALSE; + int iPort = (port == NULL)?23:strtol(port, &endPtr, 10); + if (!misc_is_ip_addr(ipAddr)) + { + char domainName[30]; + getdomainname(domainName, sizeof(domainName)); + if(strcmp(domainName, "(none)") != 0 && misc_count_str_chr(ipAddr, '.') < 1) + { + strcat(ipAddr, "."); + strcat(ipAddr, domainName); + misc_print(1, "Doing domain name fix --> %s\n", ipAddr); + } + if(!misc_hostname_to_ip(ipAddr, ipAddr)) + { + misc_swrite(fdSerial, "\r\nERROR: Unable to convert hostname '%s' to IP address.", ipAddr); + ipError = TRUE; + } + } + if(!ipError) + { + misc_swrite(fdSerial, "\r\nDIALING %s:%d\r\n", ipAddr, iPort); + serial_do_tcdrain(fdSerial); + if(MODEMSOUND) + set_pcm_volume(modemVolume); + modem_play_dial_sound(tmp, ipAddr); + serial_do_tcdrain(fdSerial); + if(MODEMSOUND) + sleep(1); + serial2_set_DCD(serialDevice, fdSerial, FALSE); + socket_out = tcpsock_client_connect(ipAddr, iPort, fdSerial); + } + if(socket_out > 0) + { + if (TELNET_NEGOTIATE) + modem_do_telnet_negotiate(); + modem_play_ring_sound(tmp); + modem_play_connect_sound(tmp); + if(TCPQuiet == 0) + misc_swrite_no_trans(fdSerial, "\r\nCONNECT %d\r\n", baudRate); + serial_do_tcdrain(fdSerial); + sleep(1); + int status = pthread_create(&socketInThread, NULL, modem_tcpsock_thread_function, NULL); + serial2_set_DCD(serialDevice, fdSerial, TRUE); + return TRUE; + } + } + } + else if (memcmp(lineBuf, "ATBAUD", 6) == 0) + { + char * baud = &lineBuf[6]; + int iBaud = strtol(baud, &endPtr, 10); + int iTemp = serial2_baud_at_index(iBaud); + iBaud = (misc_is_number(baud) && iTemp > 0)?iTemp:iBaud; + if (serial2_is_valid_rate (iBaud)) + { + int sec = 10; + misc_swrite(fdSerial, "\r\nSetting BAUD to %d in %d seconds...", iBaud, sec); + sleep(sec); + serial2_set_baud(serialDevice, fdSerial, iBaud); + baudRate = iBaud; + misc_swrite(fdSerial, "\r\nBAUD has been set to %d", iBaud); + } + else + { + if(baud[0] != 0x00) + misc_swrite(fdSerial, "\r\nBAUD rate '%s' is not valid.", baud); + serial2_show_menu(fdSerial); + } + } + else if (memcmp(lineBuf, "ATIP", 4) == 0) + { + misc_show_atip(fdSerial); + } + else if (memcmp(lineBuf, "ATK", 3) == 0) + { + char * hayesMode = &lineBuf[3]; + if(misc_is_number(hayesMode)) + { + int iHayesMode = strtol(hayesMode, &endPtr, 10); + serial_set_flow_control(fdSerial, iHayesMode); + } + else + serial_set_flow_control(fdSerial, -1); + } + else if (memcmp(lineBuf, "ATTEL", 5) == 0) + { + if (lineBuf[5] == '0') + TELNET_NEGOTIATE = FALSE; + else if (lineBuf[5] == '1') + TELNET_NEGOTIATE = TRUE; + misc_swrite(fdSerial, "\r\nTelnet Negotiations --> %s", TELNET_NEGOTIATE?"TRUE":"FALSE"); + } + else if (memcmp(lineBuf, "ATMP3", 5) == 0) + { + if (misc_check_device(PCMDevice)) + { + if(lineBuf[5] == '!') + { + misc_swrite(fdSerial, "\r\nMP3 --> OFF"); + modem_killall_mpg123(0); + } + else if(do_file_picker(MP3Path, fileName)) + { + chdir("/root"); + set_pcm_volume(MP3Volume); + sprintf(tmp, "taskset %d mpg123 -o alsa \"%s/%s\" 2> /tmp/mpg123 & ", CPUMASK, MP3Path, fileName); + if(!MP3) + { + modem_killall_aplaymidi(0); + killall_softsynth(3); + } + modem_killall_mpg123(0); + misc_print(1, "Play MP3 --> %s\n", tmp); + system(tmp); + misc_swrite(fdSerial, "\r\n"); + sleep(1); + misc_file_to_serial(fdSerial, "/tmp/mpg123", TCPTermRows); + MP3 = TRUE; + } + } + else + misc_swrite(fdSerial, audioError, PCMDevice); + } + else if (memcmp(lineBuf, "ATMID", 5) == 0) + { + if (misc_check_device(PCMDevice)) + { + if(lineBuf[5] == '!') + { + modem_killall_aplaymidi(0); + misc_swrite(fdSerial, "\r\nMIDI --> OFF"); + sleep(2); + int midiPort = get_softsynth_port(-1); + if(midiPort != -1) + { + alsa_open_seq(midiPort, 0); + alsa_send_midi_raw(all_notes_off, sizeof_all_notes_off); + alsa_close_seq(); + } + } + else if(lineBuf[5] == '1') + { + KILL_MP3_SLEEP; + modem_killall_aplaymidi(0); + misc_swrite(fdSerial, "\r\nLoading --> FluidSynth"); + killall_softsynth(3); + TCPSoftSynth = FluidSynth; + get_softsynth_port(TCPSoftSynth); + } + else if(lineBuf[5] == '2') + { + KILL_MP3_SLEEP; + modem_killall_aplaymidi(0); + misc_swrite(fdSerial, "\r\nLoading --> MUNT"); + killall_softsynth(3); + TCPSoftSynth = MUNT; + get_softsynth_port(TCPSoftSynth); + } + else if(lineBuf[5] == 'S' && lineBuf[6] == 'F') + { + strcpy(tmp, fsynthSoundFont); + char * dir = strrchr(tmp, '/'); + if(dir != NULL) + *dir = (char) 0x00; + else + tmp[0] = (char) 0x00; + if (do_file_picker(tmp, fileName)) + { + strcpy(fsynthSoundFont, tmp); + strcat(fsynthSoundFont, "/"); + strcat(fsynthSoundFont, fileName); + misc_swrite(fdSerial, "\r\n SoundFont -->"); + misc_swrite(fdSerial, fsynthSoundFont); + sprintf(tmp,"sed -i '{s|^FSYNTH_SOUNDFONT[[:space:]]*=.*|FSYNTH_SOUNDFONT = %s|}' %s", + fsynthSoundFont, midiLinkINI); + system(tmp); + } + } + else if(do_file_picker(MIDIPath, fileName)) + { + KILL_MP3_SLEEP; + modem_killall_aplaymidi(0); + int midiPort = get_softsynth_port(TCPSoftSynth); + chdir("/root"); + sprintf(tmp, "taskset %d aplaymidi --port %d \"%s/%s\" 2> /tmp/aplaymidi & ", CPUMASK, midiPort, MIDIPath, fileName);; + misc_print(1, "Play MIDI --> %s\n", tmp); + system(tmp); + misc_swrite(fdSerial, "\r\n"); + sleep(1); + misc_file_to_serial(fdSerial, "/tmp/aplaymidi", 0); + MP3 = FALSE; + } + } + else + { + misc_swrite(fdSerial, audioError, PCMDevice); + } + } + else if (memcmp(lineBuf, "ATSZ", 4) == 0) + { + if(do_file_picker(downloadPath, fileName)) + { + sprintf(tmp, "%s/%s", downloadPath, fileName); + misc_print(1, "Zmodem download --> %s\n", tmp); + serial_do_tcdrain(fdSerial); + misc_do_pipe(fdSerial, "/bin/sz","sz", tmp, NULL, NULL, NULL, NULL); + sleep(3); + } + } + else if (memcmp(lineBuf, "ATRZ", 4) == 0) + { + if(chdir (uploadPath) == 0) + { + misc_swrite(fdSerial, "\r\nUpload path --> '%s'", uploadPath); + misc_swrite(fdSerial, "\r\nUpload file using Zmodem protocol now...\r\n"); + serial_do_tcdrain(fdSerial); + misc_do_pipe(fdSerial, "/bin/rz", "rz", NULL, NULL, NULL, NULL, NULL); + chdir("/root"); + sleep(3); + } + else + misc_swrite(fdSerial, "\r\nERROR: Upload path invalid --> '%s'", uploadPath); + } + else if (memcmp(lineBuf, "ATROWS", 6) == 0) + { + char * strRows = &lineBuf[6]; + if(!misc_is_number(strRows)) + { + for (int i = 50; i > 1; i--) + misc_swrite(fdSerial, "\r\n%2d", i); + } + else + { + TCPTermRows = strtol(strRows, &endPtr, 10); + misc_swrite(fdSerial, "\r\nROWS --> %d", TCPTermRows); + serial_do_tcdrain(fdSerial); + } + } + else if (memcmp(lineBuf, "ATINI", 5) == 0) + { + misc_file_to_serial(fdSerial, midiLinkINI, TCPTermRows); + } + else if (memcmp(lineBuf, "ATDIR", 5) == 0) + { + misc_file_to_serial(fdSerial, midiLinkDIR, TCPTermRows); + } + else if (memcmp(lineBuf, "ATTRANS", 7) == 0) + { + char * validOptions = "\r\nValid options --> 0, 1, 2" + "\r\n-------------------------" + "\r\n0 = NONE" + "\r\n1 = PETSKII (C64)" + "\r\n2 = ATASCII (Atari 8-bit)" + "\r\n-------------------------"; + if(misc_is_number(&lineBuf[7])) + { + int tmpMode = strtol(&lineBuf[7], &endPtr, 10); + if (tmpMode >= 0 && tmpMode <= 2) + TCPAsciiTrans = tmpMode; + else + misc_swrite(fdSerial, validOptions); + } + else if(lineBuf[7] != (char) 0x00) + misc_swrite(fdSerial, validOptions); + misc_swrite(fdSerial, "\r\nASCII translation --> %s", + misc_trans_to_str(TCPAsciiTrans)); + } + else if (memcmp(lineBuf, "ATM", 3) == 0) + { + char * pct = strchr(&lineBuf[3], '%'); + if (pct != NULL) + *pct = (char) 0x00; + if(misc_is_number(&lineBuf[3])) + { + int tmpVol = strtol(&lineBuf[3], &endPtr, 10); + if(pct) + { + if(tmpVol <= 100) + { + modemVolume = tmpVol; + } + else + { + misc_swrite(fdSerial,"\r\nValid options --> 0-100%%"); + } + } + else + switch(tmpVol) + { + case 0: + MODEMSOUND = FALSE; + break; + case 1: + MODEMSOUND = TRUE; + break; + default: + misc_swrite(fdSerial,"\r\nUnsupported option --> '%s'", &lineBuf[3]); + break; + } + } + else + { + if(lineBuf[3] != (char) 0x00) + { + misc_swrite(fdSerial,"\r\nUnsupported option --> '%s'", &lineBuf[3]); + } + } + if(modemVolume != -1 && MODEMSOUND) + misc_swrite(fdSerial, "\r\nModem sounds = %s : volume = %d%%", MODEMSOUND?"ON":"OFF", modemVolume); + else + misc_swrite(fdSerial,"\r\nModem sounds = %s", MODEMSOUND?"ON":"OFF"); + } + else if (memcmp(lineBuf, "ATVER", 5) == 0) + { + misc_swrite(fdSerial, "\r\n"); + misc_swrite(fdSerial, helloStr); + } + else if (memcmp(lineBuf, "ATHELP", 6) == 0) + { + misc_show_at_commands(fdSerial, TCPTermRows); + } + else if (memcmp(lineBuf, "ATUARTTEST", 6) == 0) + { + if (lineBuf[10] == '!') + TCPTermRows = 0; + while (TRUE) + { + misc_show_at_commands(fdSerial, TCPTermRows); + misc_file_to_serial(fdSerial, midiLinkDIR, TCPTermRows); + } + } + else if (memcmp(lineBuf, "ATD", 3) == 0) + { + switch(lineBuf[3]) + { + case '0' : + case '1' : + TCPDTR = 1; + misc_print(1, "Setting DTR mode --> Normal\n"); + break; + case '2' : + TCPDTR = 2; + misc_print(1, "Setting DTR mode --> Hangup\n"); + break; + default: + misc_swrite(fdSerial,"\r\nUnsupported DTR option --> '%s'", &lineBuf[3]); + break; + } + } + else if (memcmp(lineBuf, "ATQ", 3) == 0) + { + switch(lineBuf[3]) + { + case '0' : + TCPQuiet = 0; + misc_print(1, "Setting result code mode --> Verbose\n"); + break; + case '1' : + TCPQuiet = 1; + misc_print(1, "Setting result code mode --> Quiet\n"); + break; + default: + misc_swrite(fdSerial,"\r\nUnsupported result code mode --> '%s'", &lineBuf[3]); + break; + } + } + else if (memcmp(lineBuf, "ATZ", 3) == 0) + { + misc_print(1, "Resetting TCP defaults...\n"); + MODEMSOUND = DEFAULT_MODEMSOUND; + modemVolume = DEFAULT_modemVolume; + TCPAsciiTrans = DEFAULT_TCPAsciiTrans; + TCPTermRows = DEFAULT_TCPTermRows; + TCPFlow = DEFAULT_TCPFlow; + TCPDTR = DEFAULT_TCPDTR; + TCPQuiet = DEFAULT_TCPQuiet; + TELNET_NEGOTIATE = TRUE; + misc_print(1, "Reloading INI defaults...\n"); + misc_get_core_name(tmp, sizeof(tmp)); + if(misc_check_file(midiLinkINI)) + ini_read_ini(midiLinkINI, tmp, 1); + } + else if (memcmp(lineBuf, "AT", 2) == 0) + { + if (lineBuf[2] != (char) 0x00) + { + misc_swrite(fdSerial, "\r\nUnknown Command '%s'", &lineBuf[2]); + misc_print(1, "ERROR : Unknown AT command --> '%s'\n", &lineBuf[2]); + } + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_do_emulation(char * buf, int bufLen) +// +// +void modem_do_emulation(char * buf, int bufLen) +{ + static char lineBuf[150] = ""; + static char iLineBuf = 0; + char * lbp; + + show_debug_buf("SER OUT ", buf, bufLen); + for (char * p = buf; bufLen-- > 0; p++) + { + *p = toupper(*p); + switch(*p) + { + case ' ': // [SPACE] no space + break; + case 0x08: // [DELETE] + case 0x14: // [PETSKII DELETE] + case 0xf8: // [BACKSPACE] + if (iLineBuf > 0) + { + lineBuf[--iLineBuf] = 0x00; + write(fdSerial, p, 1); + } + break; + case 0x0D: // [RETURN] + lbp = lineBuf; + int CONNECT = FALSE; + if(iLineBuf > 1 && lineBuf[0] == 'A' && lineBuf[1] == 'T') + { + while (lbp && !CONNECT) + { + char * amp = strchr(lbp, '&'); + if(amp) + *amp = 0x00; + CONNECT = modem_handle_at_command(lbp); + if(amp) + { + lbp = amp-1; + lbp[0] = 'A'; + lbp[1] = 'T'; + } + else + lbp = NULL; + } + if (!CONNECT && TCPQuiet == 0) + misc_swrite_no_trans(fdSerial, "\r\nOK\r\n"); + } + else + misc_swrite(fdSerial, "\r\n"); + iLineBuf = 0; + lineBuf[iLineBuf] = 0x00; + break; + default: + if (iLineBuf < 80) + { + lineBuf[iLineBuf++] = *p; + write(fdSerial, p, 1); + lineBuf[iLineBuf] = 0x00; + } + } + } +} diff --git a/modem.h b/modem.h new file mode 100644 index 0000000..84d0f0d --- /dev/null +++ b/modem.h @@ -0,0 +1,17 @@ +void modem_play_connect_sound(char * tmp); +void modem_play_ring_sound(char * tmp); +void modem_play_dial_sound(char * tmp, char * ipAddr); +void modem_do_check_hangup(int * socket, char * buf, int bufLen); +void modem_do_telnet_negotiate(); +int modem_do_file_picker(char * pathBuf, char * fileNameBuf); +void modem_do_emulation(char * buf, int bufLen); +int modem_handle_at_command(char * lineBuf); +void modem_killall_aplay(int delay); +void modem_killall_aplaymidi(int delay); +void modem_killall_mpg123(int delay); +void * modem_tcplst_thread_function (void * x); +void * modem_tcpsock_thread_function (void * x); + + + + From e5da0160edebec81d2befdfb7ba15c82430cdce1 Mon Sep 17 00:00:00 2001 From: bbond007 Date: Wed, 15 Jul 2020 22:03:45 -0400 Subject: [PATCH 2/4] Refactor & Cleanup --- main.c | 62 ++++++++++----------------------------- midilink | Bin 61528 -> 61528 bytes modem.c | 86 ++++++++++++++++++++++++++++++++++--------------------- modem.h | 3 +- 4 files changed, 70 insertions(+), 81 deletions(-) diff --git a/main.c b/main.c index fed4847..2c8ee57 100644 --- a/main.c +++ b/main.c @@ -22,21 +22,17 @@ along with this program. If not, see . #include #include #include -#include #include #include #include "serial.h" #include "serial2.h" -#include "config.h" #include "misc.h" #include "udpsock.h" #include "tcpsock.h" #include "alsa.h" #include "ini.h" #include "modem.h" - -#define DEFAULT_TCPFlow -1 -#define DEFAULT_TCPDTR 1 +#include "config.h" enum MODE {ModeUSBMIDI, ModeTCP, ModeUDP, ModeUSBSER, ModeMUNT, ModeMUNTGM, ModeFSYNTH, ModeUDPMUNT, ModeUDPMUNTGM, ModeUDPFSYNTH}; @@ -46,18 +42,11 @@ static int fdMidi = -1; static int fdMidiIN = -1; int MIDI_DEBUG = TRUE; -int socket_lst = -1; -int socket_out = -1; -int fdSerial = -1; int socket_in = -1; +int socket_out = -1; +int socket_lst = -1; +int fdSerial = -1; int baudRate = -1; -char MT32LCDMsg[21] = "MiSTer MidiLink! BB7"; -char fsynthSoundFont [150] = "/media/fat/linux/soundfonts/SC-55.sf2"; -char MUNTRomPath[150] = "/media/fat/linux/mt32-rom-data"; -char UDPServer [100] = ""; -char mixerControl[20] = "Master"; -char MUNTOptions[30] = ""; -char USBSerModule[100] = ""; int muntVolume = -1; int fsynthVolume = -1; int midilinkPriority = 0; @@ -67,8 +56,8 @@ int UDPBaudRate_alt = -1; int TCPBaudRate_alt = -1; int MIDIBaudRate = -1; int USBSerBaudRate = -1; -int TCPFlow = DEFAULT_TCPFlow; -int TCPDTR = DEFAULT_TCPDTR; +int TCPFlow = -1; +int TCPDTR = 1; int UDPFlow = -1; int MUNTCPUMask = -1; int FSYNTHCPUMask = -1; @@ -76,6 +65,13 @@ unsigned int DELAYSYSEX = FALSE; unsigned int UDPServerPort = 1999; unsigned int TCPServerPort = 23; unsigned int UDPServerFilterIP = FALSE; +char MT32LCDMsg[21] = "MiSTer MidiLink! BB7"; +char fsynthSoundFont [150] = "/media/fat/linux/soundfonts/SC-55.sf2"; +char MUNTRomPath[150] = "/media/fat/linux/mt32-rom-data"; +char UDPServer [100] = ""; +char mixerControl[20] = "Master"; +char MUNTOptions[30] = ""; +char USBSerModule[100] = ""; static pthread_t midiInThread; static pthread_t midiINInThread; @@ -246,7 +242,6 @@ void write_midi_packet(char * buf, int bufLen) } } - /////////////////////////////////////////////////////////////////////////////////////// // // write_socket_packet() @@ -269,7 +264,6 @@ void write_socket_packet(int sock, char * buf, int bufLen) show_debug_buf("SOCK OUT ", buf, bufLen); } - /////////////////////////////////////////////////////////////////////////////////////// // // void * udpsock_thread_function(void * x) @@ -305,7 +299,6 @@ void * udpsock_thread_function_ext (void * x) if (rdLen > 0) { write_alsa_packet(buf, rdLen); - //show_debug_buf("USOCK2 IN", buf, rdLen); } } while (TRUE); } @@ -389,33 +382,6 @@ void * serial_thread_function (void * x) } while (TRUE); } -/////////////////////////////////////////////////////////////////////////////////////// -// -// int get_softsynth_port(int softSynth) -// -// -int get_softsynth_port(int softSynth) -{ - int midiPort = alsa_get_midi_port("MT-32"); - if (midiPort == -1) - midiPort = alsa_get_midi_port("FLUID Synth"); - if (midiPort == -1) - { - switch(softSynth) - { - case MUNT: - midiPort = start_munt(); - break; - case FluidSynth: - midiPort = start_fsynth(); - break; - case -1: //do nothing. - break; - } - } - return midiPort; -} - /////////////////////////////////////////////////////////////////////////////////////// // // test_midi_device() @@ -502,6 +468,7 @@ int main(int argc, char *argv[]) misc_print(0, "\e[2J\e[H"); misc_print(0, helloStr); misc_print(0, "\n"); + misc_get_core_name(coreName, sizeof(coreName)); misc_print(0, "CORE --> '%s'\n", coreName); @@ -781,6 +748,7 @@ int main(int argc, char *argv[]) break; case ModeTCP: { + modem_set_defaults(); if(TCPFlow > 0) serial_set_flow_control(fdSerial, TCPFlow); //serial2_set_DCD(sericlDevice, fdSerial, FALSE); diff --git a/midilink b/midilink index 7b6cf0cac8661ad62e4d00503809aa8c041e6e1d..206979c01f28a44faf3ba2abf968a4253bb80a09 100755 GIT binary patch delta 22085 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 UNhL)4py0+5)A7^&ayZ`_I diff --git a/modem.c b/modem.c index ca72f00..a4f03f3 100644 --- a/modem.c +++ b/modem.c @@ -1,18 +1,3 @@ -/*-------------------------------------------------------------------- -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 3 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, see . ----------------------------------------------------------------------*/ - #include #include #include @@ -39,7 +24,6 @@ along with this program. If not, see . #define DEFAULT_TCPDTR 1 #define DEFAULT_TCPQuiet 0 - enum SOFTSYNTH TCPSoftSynth = FluidSynth; enum ASCIITRANS TCPAsciiTrans = DEFAULT_TCPAsciiTrans; char MP3Path[500] = "/media/fat/MP3"; @@ -56,13 +40,13 @@ int modemVolume = DEFAULT_modemVolume; int TCPQuiet = DEFAULT_TCPQuiet; int MODEMSOUND = DEFAULT_MODEMSOUND; +extern int MIDI_DEBUG; extern int CPUMASK; extern int MODEMSOUND; -extern int fdSerial; +extern int socket_in; extern int socket_out; extern int socket_lst; -extern int socket_in; -extern int MIDI_DEBUG; +extern int fdSerial; extern int baudRate; extern int TCPQuiet; extern int modemVolume; @@ -77,11 +61,12 @@ extern char * serialDevice; extern char * helloStr; extern char fsynthSoundFont[]; -static pthread_t socketInThread; +static pthread_t socketInThread; +int start_munt(); +int start_fsynth(); void show_debug_buf(char * descr, char * buf, int bufLen); void killall_softsynth(int delay); -int get_softsynth_port(int softSynth); void set_pcm_volume(int value); /////////////////////////////////////////////////////////////////////////////////////// @@ -120,6 +105,33 @@ void modem_killall_aplay(int delay) sleep(delay); } +/////////////////////////////////////////////////////////////////////////////////////// +// +// int modem_get_softsynth_port(int softSynth) +// +// +int modem_get_softsynth_port(int softSynth) +{ + int midiPort = alsa_get_midi_port("MT-32"); + if (midiPort == -1) + midiPort = alsa_get_midi_port("FLUID Synth"); + if (midiPort == -1) + { + switch(softSynth) + { + case MUNT: + midiPort = start_munt(); + break; + case FluidSynth: + midiPort = start_fsynth(); + break; + case -1: //do nothing. + break; + } + } + return midiPort; +} + /////////////////////////////////////////////////////////////////////////////////////// // // void modem_play_conenct_sound(char * tmp) @@ -291,6 +303,21 @@ void * modem_tcplst_thread_function (void * x) } while(TRUE); } +/////////////////////////////////////////////////////////////////////////////////////// +// +// void modem_set_defaults() +// +// +void modem_set_defaults() +{ + MODEMSOUND = DEFAULT_MODEMSOUND; + modemVolume = DEFAULT_modemVolume; + TCPAsciiTrans = DEFAULT_TCPAsciiTrans; + TCPTermRows = DEFAULT_TCPTermRows; + TCPFlow = DEFAULT_TCPFlow; + TCPDTR = DEFAULT_TCPDTR; + TCPQuiet = DEFAULT_TCPQuiet; +} /////////////////////////////////////////////////////////////////////////////////////// // @@ -465,7 +492,6 @@ int do_file_picker(char * pathBuf, char * fileNameBuf) return result; } - /////////////////////////////////////////////////////////////////////////////////////// // // BOOL modem_handle_at_command(char * lineBuf) @@ -627,7 +653,7 @@ int modem_handle_at_command(char * lineBuf) modem_killall_aplaymidi(0); misc_swrite(fdSerial, "\r\nMIDI --> OFF"); sleep(2); - int midiPort = get_softsynth_port(-1); + int midiPort = modem_get_softsynth_port(-1); if(midiPort != -1) { alsa_open_seq(midiPort, 0); @@ -642,7 +668,7 @@ int modem_handle_at_command(char * lineBuf) misc_swrite(fdSerial, "\r\nLoading --> FluidSynth"); killall_softsynth(3); TCPSoftSynth = FluidSynth; - get_softsynth_port(TCPSoftSynth); + modem_get_softsynth_port(TCPSoftSynth); } else if(lineBuf[5] == '2') { @@ -651,7 +677,7 @@ int modem_handle_at_command(char * lineBuf) misc_swrite(fdSerial, "\r\nLoading --> MUNT"); killall_softsynth(3); TCPSoftSynth = MUNT; - get_softsynth_port(TCPSoftSynth); + modem_get_softsynth_port(TCPSoftSynth); } else if(lineBuf[5] == 'S' && lineBuf[6] == 'F') { @@ -677,7 +703,7 @@ int modem_handle_at_command(char * lineBuf) { KILL_MP3_SLEEP; modem_killall_aplaymidi(0); - int midiPort = get_softsynth_port(TCPSoftSynth); + int midiPort = modem_get_softsynth_port(TCPSoftSynth); chdir("/root"); sprintf(tmp, "taskset %d aplaymidi --port %d \"%s/%s\" 2> /tmp/aplaymidi & ", CPUMASK, midiPort, MIDIPath, fileName);; misc_print(1, "Play MIDI --> %s\n", tmp); @@ -864,13 +890,7 @@ int modem_handle_at_command(char * lineBuf) else if (memcmp(lineBuf, "ATZ", 3) == 0) { misc_print(1, "Resetting TCP defaults...\n"); - MODEMSOUND = DEFAULT_MODEMSOUND; - modemVolume = DEFAULT_modemVolume; - TCPAsciiTrans = DEFAULT_TCPAsciiTrans; - TCPTermRows = DEFAULT_TCPTermRows; - TCPFlow = DEFAULT_TCPFlow; - TCPDTR = DEFAULT_TCPDTR; - TCPQuiet = DEFAULT_TCPQuiet; + modem_set_defaults(); TELNET_NEGOTIATE = TRUE; misc_print(1, "Reloading INI defaults...\n"); misc_get_core_name(tmp, sizeof(tmp)); diff --git a/modem.h b/modem.h index 84d0f0d..af8013a 100644 --- a/modem.h +++ b/modem.h @@ -11,7 +11,8 @@ void modem_killall_aplaymidi(int delay); void modem_killall_mpg123(int delay); void * modem_tcplst_thread_function (void * x); void * modem_tcpsock_thread_function (void * x); - +int modem_get_softsynth_port(int softSynth); +void modem_set_defaults(); From 898957add80a8c864d14f19fa4c40f1e6a45e0af Mon Sep 17 00:00:00 2001 From: bbond007 Date: Wed, 15 Jul 2020 22:34:02 -0400 Subject: [PATCH 3/4] Refactor & Cleanup --- midilink | Bin 61528 -> 61528 bytes misc.c | 62 ++--------------------------------------------- misc.h | 70 ++++++++++++++++++++++++++---------------------------- mlinkutil | Bin 18296 -> 18296 bytes modem.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++-- modem.h | 1 + 6 files changed, 101 insertions(+), 98 deletions(-) diff --git a/midilink b/midilink index 206979c01f28a44faf3ba2abf968a4253bb80a09..2d3bd2f775f4e1081effcee114e56fe0700af36b 100755 GIT binary patch delta 22105 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 KBH6T3Ph0`5dU;LcT=$WJ??(<&*)Ii zAFkvZN{SBda~^8_lSpR$GcglneN2?F{^7&@)_3($$Onf#QXbB$-#kqk!fm+mL6cTY z8(ow3XOM;<_O5o7y*$9TW_VcWl8ZABbt{n-zU|9Pn%(>OVY+a94owheVPj_cZ<{dUbNtj#`l#C|`ACqsNXa8vov#3SIyEc0zlt?dP8Lm7tY#!^ zu@RV`o?2T;mnee|)m6@e(?%ZpSJGI$iKB^cB9?R08nxwk6UJgCo-1sr`lS+-)p)|} zqsG${O`wQ%meyvzKWS+r$`l<*o6@A+gS9A788*R+CHz7v1`+NB(-(c(U%yLxYMG}` zQFc98JN~ti*4!ox&5!NP#~9PI7z~mRR_tPHOzHBZRgHd8v~Ovvi^OF^vDMN@*{}*L zXc!BE_kx*!C8#xjBa>VPuA~-XTK=$4ObAU^8H%XO5byrw^a1Q-)Pg2EC`}naBDA`o zsd;;=2~~N-L6@|neU&NdI^|Jf^O2?KT3*9_gTn1uU&`1?iD)RAI@T%<5zZ;(9`<1^ zqo!4+x&-SZKA6JZ*Hab3-}ivhcWx{71=J>GDCg9y#ORXX31PU9_1AiAUsm|N+$q_8 ztHZNZMPhCR^=#_>PN_qI+j<+|2Xs^K^dV{^b}FTa{nm;EEZG&S3Ra}06&3yxh6tD8 z?7P6FEf3|q>TC!Ptet6c{q;=-X=SbhwRCPz3;jM@`FG9Q%>&} zbh-Ii%gk}-Dsp^<)EmxMm5T>oyZNeRi44o{SvDufDgE*Gw))sw^I4ZPxs6zz(xvVq z`!b|-#Tj)(aQ0Dqb+@`CgVO#v+N<;U_~qkFf5espcTJOVPop*?u90L+ZVbCtWRF{D zP&(5zv&>Os&soT?T5d>mVtE~dfu+2h(vP^=K^Z@Eg@Ikt%}-sCkuj@V1DfU&tZ{Q~2p7&V{DFGr&|Gmuo>Q8L3VXpYPt`$>fv~`u(FB++#SWU>gD4TaW zrIXzyc0zrPAaJ_qDJ0HUYw4|roDOBLxHYh5I#=n~6OL(L~BKrzG>>Nyc*yX;tzy)^J% z+f7VS4Xlpna?Ub%NCRuy*HC0nSO?o^7Rx$Pq_JDe`S6v=S&!nN$H1{QRQxTSEVVHd z%U&F&C^pK{PLB?_@RpY;}rR|GBYa_8m#Q%-&&b7BC$)S zXSvWjW?12kPGa0DPczd2`e~(}J=Do-AIxF@*unqm!Nk|c>kNM5gQ+PWc1{n7`gsQx^*|>!#Ie2Ulu9~H_#F7@u2R}Gaa}Nm;Hx)dlN~aio|r%<1}F-fl=r|F3atz^`EMGrD+~Lw}#W`oYK;c z+*doOn!nh+G!4`v1GvgAHQ)+B4vY)Hq9i@q^y@QW0Gi4@pHs=g6z1D!A zVCblCQ+2x5ndvcyM$5fi%go~2!U%KZWqh*DPAQ6ix@-h1uj99s8Q7k7{&3ki+qO;4Yb7{ckf zAqFdV1iQ#isk(g)S7;v|OKB|a2vcZX>L?ad=#;G)Qy)_8l&tLY1qFBskitH;MQN}|l%uuY+V+c$u7u>~S0i&+Ez0#lYW(~ZZ!rKmp-~Uj8 ztr&+u%3%*fht(^6;AZuzymEUnPI9QsRI4Edrsjwo2dT5$nL~c5#r9A|>ZbxyC)bBU zYONymWtK{g-bw`(0VhoCEEcab5r_O@Jz)#5`yA|A{ttGZwtjYJeC%@WV)wT;YNVEb zW7h(_t{`?(p!dmu6(+jlYxzGtY=I3*hbcDDgmOpzwdu(r-#o#EoiE0jldnqA8LMz% z)m1De#aexXa2ZD&rux+8Oil~EzG|*GOmL$ zCp01@!ZtZ|)Md7|Ly(6;4{GuKvokr`oz(ol-^zu{xxSx1s;u zSPhrvyKw52E;})&_&QJ`E8B0#aXV-VrwKd(oj$wGaD^aLy>La&1Cd``0~72Dh_f6t@CZ2Q~7>mAr%rixIpp1)m;tu1vnGs7=8J8!qr zh@xX~MAXhxy^^LgGZr+pbtU%3@kpG{LPC1H9-0Fht8NoL4Af4)uLN&ub6tC6Gd0g@ z17Dyg>moK0v;N|C2>3#!f)qx zwGAP~;*tzL%9^Y#7Gw1ML~D|*t<3}*oV=|;*iURL7UT5lib2ao?Z7~_<=In)`83xI zIp~l)J-D9`eoox6kxq*~VKh{Vkz52HVZ}wghp9X`jZTefh~-9NU}K!LEGEL{jm2Vy zkzQe<7#Pa6#Tqe3&tJ5zNxA4?_h_6Ezl8Aw$GnB+P~IU$_JnbaA^Oft#IpmZOZ@$- zCqwdFBfn#{VT{Tf*T}e$0j~W#ZG6+}(Xnsz?8o)mA=M6PdruTTxANZAW0_EX>>54m z=-~_36pxI&t+8gwqs*g1BEB11BXX19~;PMxB7X+Q=-DSH=yV@z z3WnBb{ny`4Y(_+s`-Hy`uFGB%}w zU-Lv#RCzr{`Q9icRQQQcCQOsN!Z>1Dg*zSU{W1{2ATg0M6o+TQxE7m|_crcRE{@H? z#W5Oj693|pBbZhE+fR;RmhdN@9P{8eGP8%7!eqTWm*LMgMc>h|IN1m0puT=eR#5u1 zDF;KcqKl@pjZY%&ys}WC__2Jt)gcx3(D-h{Ot1>SSur33P^CPQz-K)*hX`x(Bz@l$XM36>QszjHOd|~mwlUgTl4l29L+;6=oEDgr$M;Lfm+6Q>le~Z z^+=aIVr#J(SFHo;0(ikg?@gAUh#C7*X+@WCUkp6^f(SgbrrWv5ni$08UlvqOEmRcqy zl+zwjQ~X?6JI^D@9<3f1-QP6B4551Pf!{ONs3ML8diM=v(?B-Lw+*br8ufqpBNd|_ z&VWS|e;|`Wu0zjk9mbc=%MR&L^q~-k#G+r0uOIzQ)kz4WVliW>IH(8i#6(bK?vu3` z>l%ns1X0o;2n`AopT0KTcB+GzBCnH4CvOys8y4mV!t5pdx-*rFF;L7Ou_bp1vV3&I zAq@@iuSF$xCW!wdo@#_GtLSt9ZGN5T?eX7uKE*Y9hxAg1HkpB+P%r7}g!W6M*MQo# zFxX8_pe~L<a+1=pJIpj)OC7W zM@LP5@-9uN|7L?o>l0_wHL?Ua$;s5IEu|xFMmK}f?Q?Y7t8`Oc z&?9y5w)J}3b<}>pYGn5fM=#PDQpzeunTU}O#aK9S&z_i)Ri~5$RC|x(c0s)9P9r8T zP&^Nc{V`nUx(QS1u#ZI7=`|w${^Bj+ru#d{V{rNsilt%{u%l@<=C%egf|z zibve)gv14H4(Vunf9q3#ebi1{&G?qxrsLpPnTU5AEU=+M{XLb&Ft*SkT|j_kUjySu zz&J$DS=aPQr6?{9%J6&*lbFFuxJ?OAV(Z&86&zZC>bP6iP-(a20WM%G^G>4`CYlJ3 zcSwiZ&MJ#?3Q56@G_RSyX9=jg&S6UXP}$cby2XkWhT;rim6Ia1z_GXVuq5Y1G`34t z9P5)Z_z!+J!N#@i&yW0XEX^v6sALtZ>X4Q4JW}@Q{3KeW(>I57-$|N$wOp}1j%=!p zrkF$zaSEX(5#GY_x(@FY>X3e=zzzV0AAb5^*$V6=V7UQUT6?Gs)d>;K0K6aoPF7HN z0#+J;#bJpwm$&t0L(@N0YDrM`Q&U? z5bc1S3&8%Nz(O_^+b;%S7ZuoOz?uWF&lQ*vu-*XdqykF?jKuhP993YG0E@B(fFCNr zDF7w~V7nFAOu#Y%upJ6453pGQ*j5F0FJOfM*aijm7+}i+u(b+oF!qsla{**lPjU5(VZ3Y<~b|QD7Z_)dpa6@0|$W0d_6`o2tN019l+*%b+lu@IHW- z13+xOqVp?&H3wh`3hWucx&pAl3akt;R{*9}VDkWr(y6AVp-t&g<#YjCXFsN4X{yv1MLkjYf?gI-J@ak$%atHd6jMd+eclUORW@TDv!g@5^_ydh7ZKvU;3 zf(xGw{C6*nF?MySmbCRI;m+h1pCyKalUw>$xy^6qN7%}kL;Q2LG0blMU3_lm>urzm zv#Mfvb5+#XLWP2+URO$MVt+n7tn1H*eUgqZs~SE+?DUVmNBYs@*G0#_UNzFzsGra- z&!P6wg7e=!CHB3FEQwZRGS=vp7J^Rm z&|-V|!hU5+#0696nHLV`ppYr31vvPb&NLXHh91@vk2F&*wx6`=i^NLBdJs@U?@SKj zfHD~|8r0b>jRMK4EKC*@8l=n+h)>dTf1#Os$3nlIwZJY`MuGkkL2YeWNH6=Sw(E7L zEAXUEwi2-vWHZVJl5KI9*sJcN7yAH@pr<5P)w_X|HH+D)3@(>L$q`dHE>{C9oZ9w+ z8y!H`uIv0?V!!I9Vp6fG$W&B38Y-?|OjZA9sF(}ZKup{9jkHq6rm`Yu zns4*VjoMm~@0Yz0pkKV@ZdI&T2Z&Vyp=HI~qpg(MAxf*pK&O9-fq2%ZgsBRqv_-k47#L~Oq z1n_X+SKS5gbs=9FZ1cqZv>_0T?EuHH^ThK5pg-cgI3a(Y*fs$A-xT`LJTWRZn8F1> zW6J2i(*l%%p2leEc#7xD6V1S-gFq(GOZje_bPNo$7gJpl@Y4#Dd!D!;R$anS!yN!I zdaqw`MBM9NFf5o@d4L#oLbvoEphXr@VTNIjA;Rlb^KdiK?^_$tdLiH6`fTGo@p`Py z-}n?7cLW+2q0tmbTcr?Ug)%fWsV%xOPh1~pTZDepG8YtxYDc$pFQDO>|DV`hzTRX) zxJ{AU3A z2HL#Nk6Y)7B=9adU%~tFe*JGdD_T+Z1X4&Vj^6uI$ZYB7BlwqBWqxa2anb}bTWSHc zH~{_D*?JSTSRh=#bUt&d*q#u0KkIx5@BR2EoM|=y`a2wP4js_pzno4GJRe}N&)EWv zqZq#!Z#!QAet-Mx&hdEPlIL%K8DcV7_9n-gd8?%u|DDF) zZH=?8>J|JmFKDHEI^9y#doMUxKg8SV65z-SyQMvE!*?Bn;ww4;rGZ4KFxgVr8?N zNS!qkN_S#}&6$goB;=C5#|=bXUQ9Ri;|A zxTIAs{zhxu{6N4LlN~YeMPz_EcPgSntLTqdc#l$66=9{+$#i zn}7G6&CwlgQ0jDAPZX4D;Zv)p=q0K5wkG+U_<*4qPBUG8|ibtD8W7SwAbsfN$nZ5)!1Rl+&(u}wrDRxL?~`E|SJ4&Hkk z%Iq=qL=KXqUAOs?z2o?<-9woVdGGGAVaYudaRZ;eCkrL+gM0MYk>9vy*reLLdqp#n zHfjk-eShDX*0_(u4}WkcGgSMpz02}N{=V=#Vd2xbzl!&zO8EKd7kkF1Ozfc+`qjqe z$$8?|$#3R|U!l6^iQ6Xo>5T2;2k)I6eWj;gqD%a|z0;>Ochjyj2Xsvt(Di$n{$_zP zrYxtxJp1|6d&e@z`JeUOF58LDP>g! zR`-I{cu7j*|FCa-4(_ZZKX}dGYqov=(Yv+FlHa~Xy&_+XS?kw0RpvwAv)F#@q67Y$ zqmpFk4jyn^aq}*`H4b%?*ZE?jrWF*BcP4O2Ik{tn-vu5Upx& zLjdauU>(0U?v{jKrq08^@$iS1Q+nB(__V-~uA7qd;ayFY-a6}lo?}Nm+R2`!E0!>B zP{}s#O)hfYO9hhj89)1gKIEfW45_Yr0#6m5YCOmB zTsh4QCN6x@onc4;9>RZjWHX=iSyJ7yqld%m5*l`C>molr#SYE>0+bsVG7Eo&yqNf* zIzCS`Vz@*|I3QO(S05d|o4%;qeJYXVmz<5`XPs^4b3SXXtNEfWmMO1$^Sg&5_}cGQ z@x4FhFk9*-{Ldh5%xg!W0-j1dAL8K`{ydB?{aLF#5C8lwQ(Nc$`Qy;R`#)pIc|2`+ zq8jS{)Uj2Ae{ph8&zO)c_*YI___7{jae$D$lleD#l0%gL8_OT-F@|J+$B^v0 Jt35X6e*wlqqA&me delta 22340 zcmZ`>3tUuX+CS&a3^!2$Q4tYl2G9Y#3_^yNl0iM<45Fa9b`{iWyrg)ksN4pXHMh0a zI%cHSy4JR6Xb@WBC8*qT_p^3rt<}_`*5nSyiRK*U`u*Q?W`@CUKYzb7&UxPFeQxjb zJn!>fhASIpKT1p{Tvz(A3^n469U!@N(#tqeAT6x8I%g=7BGsW{*A2-!1-C zwNyp_P=Asx97E@Dim?+cx5Ue0MRX3UQc|+v1gJgf*i_a}F9IIsgU1EIp9ef# zf>UamcY4U@FiuEVU=iG(=X-^!?oeH>ZQ<`e%UANA{fFN>6Jc3;>DW58XHa+{J2!%{X~7I#ZJ zDfmMkt|>{au6I||Q<@gDf3cX zaW`f53bqcTZSN)pt7ZMVKjNYJldg@tf@*4oDx2;T(|jTqc{cJ&>QrlWCqo^^bG?J1 z(%8RYplaP*nR?XB-3C9AEDx>r3Kd<>R-=9O9HV`Cp3$CdfN>Rg7~g1c%&WtW54{~V zM8S{cK2hJT@w$I%{Z3Ry8KBB?)+;>Kc|ZFm$S*-S2kff;$vt4#>+U=4`Y$(EtkFg& zml$mqijDR*1E)?Lg+YUeRhkrf3U^*JF1$w!CivSRf=~Y+f*agif;KL+)ct;|iqGR_ zYR6e}ml*9SMY8NZo!~kQr{MG4i~1_y?JY2Ebw@~|`NrF3I%}MDcK%d0jivde-oxhk z2_vl-2)zW!*lJbs4W8sRRK~XE|E!_2BAQ8$Yt5PPD+QnJ__mcm1#22R1vF?r%iA() zjUnNGf=E#O-U=oEI@hgD8hRfzy8zu~bB#$JtNzh7I^c%aTn%@);nyy1Y4Z3nAG@Ay zr5erukq5QuM~1qLM+v{?lGBQ+b@#J>%~y4Xge!Q#3ys74Os235OYQgDfOz@~LF~4> z7Tv+F+*RDF-mBmGOwxXzwE#LyEm`O$vUplbA%u{dxRI%OyQS+9;23xEG?7 zK5$JTr4oI=LUVYHf}hw=KCi;HF6l{f`STi?$6FHIg6O=Yr?q})or6lAW2=jibL?pt z>cci#p8^S*H2(b1icEPa%V@t&_*zI(K$_lzW(EJ9!z<*SMY_nu3{BF#B(j44%0crf zP&<~=-!$4QGySAK1w0P$QB=xJukcPgW%dYpy+mz{bhJWB^nTnL;4;7+Neu&ySw6E` z6#QW{*u{-}D8+!t4vC1m*J!Vz8A1Sv3jSr_+yqV->(4M33DxmKIIzfb-zVmFW5lAPvYLxjhWpPz#R+RNWWK<- z83+x8g09Gt<5r|1LZs{gE((P9YlJ@u>wjR7t2NqZ{O_Mi?<)iEXQcNdf%gykgD1Q9 z`aeXZ>y)KjRgsHsWM&bsO#H zq6hpVlKUZjj3wA(`bCOggnmQN@4+)3;k#bauN?iNg8JP8)+}H}0xKWGvmBm6Q<&L< zDo3*q_9|fhsDnqaE+-ZwPFiTp7bjvP#X9$)-+s{!*L_^!J8^}CJWmx)FR2ywA zrSj0M5M^z=!qU?oAy=Z-(j^D;j6j)E06IrNCkk{?)cE-%+LDNgM#lMW}|I@(M5^T6z+}5lV-$uX_5`#E4#WD^*vkW5mt5; z%xep(A7#c$A7jeL*yVY3%CwN{P^B%In>8hY{)oGO3dkWNR8L^oFGcF`gRJ5WMjd(r z+J!Rwdmd6sL1sw*7Y}_;aEZ!DrR9`FRpbnmzubSOEE-CJp33r1l(Y4tr!V%z1_&5E za|g}#3~%r>wt9r|C^S97=N%a=wYQjEz|vrr&HYiI6mk-j+tRp``o)&}`>A~ly9CWd z7Mpu(o$5VEJw9PBBn>&pjs=@csqLow?K{|9b2ouaQx{=Vf_fI2()Lg$wKrMH{bU>o z%Zu&^wMQ7$MUpOH6vIF4rF3swCHz=hRbZ+i$RD@?=3dXtP+fwI*SRk;7Y(&y{%Z=> z{*g!A1KKZl->Ll~H_<*@66^{^L@kup`j-j z-s2h=Y0-7(NcXzFXw8K{YdgMajXFiCDgJvWrAB1Th^6>me1%*}il~v(GR8e6wydFa^bIAyNmQ?uzpWR~w?*YD`3+osR`R3U z3`{k}kwHpk!;K!){D>AqG8-ZleWOoQEC}Ns4@Ih7ipQ?J~7`TbhWEBA#7T{ z0)^zRJoYNgYZxDpYnaUnJZ>m?Vzf;(Q&R}6{AndD7IDQeq zCgzAX>4*n~c!aGTI@V~b3<$r?C;YR+oC-<$&)bRg3%KZMCc|+L$q}S{$O@#~IZkR) zp%rdLieFHoF(~iD|0u*5{6-f$@syRKuoQAA5V zBqjS9nU<7N|NE%^p4LL!g%L)3KBjy|GFTj{xaE#Eq(f{Llm>^kS2Wt!mq1p^Er{L9 zhN$yumaugTxxlR$gxe0>Dj)7ogK*O=K&~Bx{4XG1@F8CsgdE!#bxH|$*Doe)75qMT zUIJx}LJZdUAb)VrNw7ChtfMFX(oBT$3g4ZR|6g`4=TxDMnnM&DYgRPmoF_smSqrf? z3HK^V#zOnLIpBV9yyf6P_}>AejlV{M@2=^(+&x-*`=R;RihMlt*( zM{S+aex(#|(wd1(WTn963BF#jv( zh$dyZDn130Ul~CPfkTkE=1Hv2xF_#Ti=m!YVFku0X-tsJ5a_antG##Hq^})w64WKr ztP1|O{Sg{9HuxGG%o{ZHGkAv_CGV@8LyplBB!?R@J2_*#SI4@An_XnfgtRjbM(q~X zca!hmI~MWYtmKkUT2+9piMU_SOx(*)8OE<*-I}$WgDbj{9-; z7|S+KS!?J)qg}rVwCQ*Rf4HbOlSLv)2_!Aic)!&e@~3j_q>XgUG*cNzuz;)Z1pzGU z83^@5on!=4ut)3+Z>C_*vL)eWaQA6huHc zgt$Wvjs@e{eUH5#%=)n)){jW6Tl<4qFAHK_IEeKEv=HZkGrEO5unyCYtxLj2mDgK_ zHH-^Qgz-OIlVAzmCOxq{P4nBl8oXaA9qHQ$qxn=2;!C5Y_aW78A+no%#&t$Zco6}- zXbDfQr3i-DMLv~uCs%GTGm)L#Plm}%PA3+e{Q8Ti&^?rx3IT^(qxoh5HCj22R1|QzD@F4n)tEO;(iHp!K`kSdiE46!xV>Ej#pf2u)B% zid^hmcEU4QHKbkhCcgv<=UYuSeSpJmm(EOS7+&;-*7Dr;xk+=E0XO7YxK;rk47LI& z9rs;ukCiAE3y_#(2_JMdw~`z{B_ogJL%Ud$tt_0oHCIP}$PJ%2Hl?S15kG~QlQ4B@ z8q3GIg_Mq#)&+=Xnm_JJlm0*I;tJ=*nI3B2$x^zDRy~6LAMLbmcjF(h_G@aLoYCC; zUOAP~Y5h`Oco?V&$A3Nggvr+QXb3xt^HOEc=4vz}2-n^RY4b7f6MPG=OEY@AuiH+yGjh_E|+!Y1vYlJ#tI znOn$iXK=nWen&|H#cOs%XjRBoE89b*jpSF|hZ_<#F^G`K?IznhbPO>CQnqDQ2hl{i zGgCr4kNsMT?}Yqhgp`nxQO=YmSG9R63$5nS{FhEZRb*(+^k*Ota{1^T-eTgl#9AWA|v~&9N)_ZsCJAB8mpNhEdFb z^=ST}BZnOdw>aO%Zzoo_EX5Yhu=k1K7vNj?`%B6foh6`InGW@d&*@vFoX|i=?`hCnLEcdnl6Yxlb%Hne>mr&qq zFjr#XVi+Kp>w~`oc!D7`fS>JyGruD)s{WnHHn(V?SBkH-WEaA)Xn=j35B@dqmjW); zZhF=wL}Gh9mw)MZ_0N*Ahto~-4NM4Zpv0rnWP~+VmmD>#$&}_eX3mtp6+3=y4Y?f> zM%*nIgD?@XLGz3HRctQ)--3gXxJGKymBxFdbAg38xg~tsw%3!$iEXlgxY|6k&SYC1 zEpKAc{GS}eoy-{1fsrD^lzXDS+=TyUols08ha>Y`vZ*_^tB&f~2rV_iCG_-?fup5( zAp;jKUPD2in@yA;RChYbpq1~~@{A^f;{UeOl8OJG*f|xSROONkMRWZ#EWTf;j5%S< z&%{C97AYd$s2Fg^EBcH&as+r?X+SMYd=j$Hq^1W(ZN0fGezaF@V$SD}KA_R`Sny z-}~7=Xcn558DB$JGID;#U!(N*2oDTm{CD8^8Gky6@wp(zC&4&ZVw?-cnza>()h~uFe&m?Vez)tblAw4z6H@%W2Hj`BPFc=R+ z`99o|p!nZc$WhKnb4<)pP*ACE$5CEk53<1Qz7I~o|E*DSWJ>`U<`T3$--wZnN4UpH z!Y~GbM#AJuH1#U6P`tUg8YC1h;bhMh#Qt+AZ7XmahqO`rS!oG|?-;_9T?+Wmte~YA zF*${68nO?OoC-XPaJUN-fyN)v6#s^oMD#vDsZIW2!4@%0cfA{i#hU1tZ?Fz{X(<71 zgg0L)g`blG10@^9KP|1z{18I#p&s(Nwkwki&h^+c=NDQaasNhEsf=gn5_rPVjUR-n6dto14qGBrHvT)++yR>Dy5=LZqh@ zr(61dTk~M~14!i0)aWevBx{?y$)s?kf|v+}AsK6>)+lRF;fTUy+nwou+wWDw3x4Ou zbUf8$J14^>&1bS-wlhzE*-kx8^HZ?>XO$K^E@4Ku0@k~?`?{n%3PQ=xh^-|3H$a2s zfib)mSmC&Y%blOy+}p)aVK|Bok*1s&I94uy7aWQR+Bb~|3PK@MQ`6Z@mcECG#(L>V zLi6u_CfY7ZuV|Ka{q6f-<;NVQCU}L#2+P@hPaQYeW@W}$-Xn5+VJxmDBwfOb7~{FV z{7#6I(R>x_Pl_K3jUR%>CD6F|J)$XkGDKTK0paB!T3+f}>(lar6fvYN&RlDxa32XV zV&9D|Kt_p61Xvi-a8S!b?hE3TGJiTGUi=^|;Ge^mb`p1iNYZp66QlTVJ;b-0`-A5< zniM9~XJQnlnkXl&xAcV^AD9tBC5XEQ8PyRF0jaz~Xb;-l{GyQ-y(bK3)&p@A6C^=| zPlD`061?PLpll&dJmifm?65ct3A6jo){s@ovyMXB-mF4fl^)6>K`_)9)GT*97`_hY z#Q!=g^LG*cWKOfddl>q|*8gjr&s}#K=Wp%-<9ys98>eKDXt|PcW)3nADTe*VdDcOU zQ&uxz9LpvN^f;_D7iYJXXXSDR#NiOTpg%>n+(-?EOytUBSZQ=<;uH~*YxLvl#G12x z>2NDnSH#?D+kYw7XEgDnw`XSL+Em-8l0^}cgqXzumke8_q%*CKFQNFq zxV!>)CTRS(^eb876#d7jNfiI53VM>I@0Vj9p;1(#0_`~?`>W)uFd?IOMOsxEL8gqf z%_BoV^u(iFXP#0*XIc94-%pcFZnEu@rbvjafX7Iy&wH`9?=p}cd1U6dNTbo}T4x@Q zqcT#+V>QyTT1Wlz5;F2&^5zl+I|5jkT|Fd?(eKNJze~u*kes&Cx{_uhE{HQr%uyxx zClp$J;wd2tMnAGh%H&W)@&PDe8FohPV;j@K=u)l_ul02bBRXA#KfFr{0pwh26M>M< z#Q$G%720yLB=1p-!Taw(uC>7}yzM2HD~5GQpA@32l2Dac(|8M`_>C^&XV2a$v`8s# zpJZAPX5!56`jXqNHMh_`%x<+gBP2Z5EPY-+_ZL3R{A-C5b4|K$D#fCfmgzcgw}vs;Ysw+F~ml ztr;54@e}R~BK$N6KZ?G&r30?2mG&(1tR~yGQsR`d1CJt53(EaTh5xu+jR3`WCrcO57RBXzk7F zN|4$UN&H+=KlpGuBY#pZ?SRbq-U=wCf;LJPK>ou6Q^23uc7+cq?JZghZtV& zFxhTy;;uiSjduz0ZBY%hTIO{?qLtHQvM;5%xCix*VT~z);z7PL=u$1k@9L+dPE?~) z`2J&}KqN~aRk6y>N~Kc>>s20HO;H;9n2t^68O2ND*r=Dp_*^CD6fX4=W>lm8R4hA` zHQD#l+=&Mh8LF52>Orlx2P?m!r2Ul1UQHjYWhAL-WMXw2UF{Sm_teKc6iW{cL+YcI zq3nyb7%496;rUUQX@5aZ_nYiI&As@rmWk@&PCPu37P)H=kJWde%G#q)5{e3wT^}v4 zIB1x2il}B4;7T8xXB%a33f}^=Qo)UXM4RwcccJ}KGV#w$7AqGs{hCvF3nw;C;Yc^P z`jKff-jNW9bs2@~4Iu1u3R`;$?G$lO$|*e6t*Dhw6>G^^f6ggo`O-Qv)Q@y=mmf*D z<>! zB958uhcxm@3}L&x&SZZl@V*|`^dcJ>HKoouPvfB)8Qq(WCi_LyKeS?rsHRK`dySf_ zSdo~e?M}m;X{MTppWDU2)M_Y}y(CmpY^<5i7;2rtGT7~L3KzOY!fs`G1xw5s)k&M@uth=U6r1TO;Wy}%O!hue zIov6fcjD4m_Fwbt%~T3e{8A@5;NrJaFyd(qS6doyiRsL3Fv-_GX|IrhYvQADt;1x$ ztf8O>4VVT$36No@HBqM+c7Ltpf;2he8R5~eYgvWbDfDy%(+T}Ablh$~otqLJLR~oz zw?%M!)9n-IF_SW!)afheiZ;x4U>kH6TVufqj`**bO%OJ&%bX|)OnCi|?cS66A7 zV;x+|s`1*nT}pO&onlR#Ij4@WUDiQv1v`bxPU3qedwwRjepP~HR40?HVm0h=t(iS8 zM(Lfx(vG!kzI|jgL5}GRl{&rLX|kKMh?^!gsH&Ve8Bicstk2Y9K#3ks;a;3_hU1Pw z4l+e~rpX>QQZg&bA!77PZk!*W7`W&**|Cb0`gny;+$M{?Ch&R&jh6#&^->pd`sx*q zOWM0K%cLtgWW>C}+ipffX~Q7oNf4t`D$#9_)1gh25DTcI7^HeZZCNCbN4{ z;-(wmHqOWHFxcsX*xdzNxH38IqsuHvA#)_f6(Q{LUw1q){D0Q7p%+Z{9D}baP$ySl z-QY6WOAKb;XzXoQvATw|mP}R&U3PTTHl*Y51@{E-W^5;$i?^G}_}<%2Lc_1)c$XTx zy*r*LJ5Sp5XuGbVEWWsjbJ1$K(PUq3SnC_Yp}5W@L#f6x41I|~1#_Ov-Cs)#iHD-b zJB6|i!t!3LmR+nNxge~)q8>-Z_%;m%>n~RF?P7a`ZV^9&W~w`{I)#vSO4n))DN}16 zBaNOmO4nkYmp~tl&=T1?+C$(^nCse1_9mYl2>ddx5jlmQ+Vt!w+-Xv?n3ks@Px#(V z_Ley&dzT>uQwKyNB{_Vv#!0Zbgva76J3456oRvrw!sgTyNB^lEW?LC@REh8L?M$Nb zI;>zzSctTd+r(%1A=I}zor_CotbUzhkm<);z4fev`r_Z1M>EOP8d=`AZX$cvjICEG@*?c(EptssV zZ`Fg|Y6iW%GwAK~pto~DZ&V{D*CLbsLNP2r8yQ;;_eb*>h~ZlHXX42rDmH=TSQYmo zyC28YCVS-ip|?w{asE;%kfRN@rowi9#4wkeROUWC_NDw4||B^$XHgV=7cr;M2Tuxu8^@S_glfnMBr zz(Q>Ac2WlTBqbiqjA6sMa5Jvsts)k~*>_rGV>D(4uTbkC*N`cG=XR6*=907cPB1Xe zn^BP!ag}S3LI(Yf`_bnCsUbP`5T~K59zX-bWxKoG1Ut3?uicJc`j>x>e##c-)}{RELtoLrE5Om+``kWe6S;df_w|A*$|)Zn+4S7u)jXOTYh5NP z8_6K5t#677eNje&mHar+E9~1Ko^(|HLLYcAoX)1w;~ME?D&q(10;zGA_+4u;)&d0a zlISP#eKV?jC8L`}^ zCq_*AxQo)w_X#q`YCs*}6&8r69KVy~@Jn%TGXv{x=IWkE!tgiaa~ya3iP7^P7H3H5 zg0wybN*^`nz}41vktt?7ITwDR2oybn+k3XvAuQ`5$-<5)GN+XMlu%w-;Ac48q78SM zNKV6)R5HE{=98uzhp@g&#a7faxN<;Tcp?f;hmg`mzVltRb&n?M=4P;DYd9i3tTC~y zDo>Qs)lb|%H;lEsBbCsp`s6aySSu)OBhzXT1=H%4Ci_+_eB~O!BeZyQtax6j`%|O8 zgjaM{DE=ijQI&wyV7)<8W%FrWB6(B1Cks_ACG5|MqSt+ym@Jg^$xInN*2hh+p z2#u@Q7)O_IZ)}`ksTX%QhEu|qAUy42dtKSva^agptlKVd6`6v2TX9mMIdj5uTKX($1qEu9j=Dv!&v(mYYQ&?6W)>XrM zGr7NS8oO!+j1&JIHAQtD*3DL9+(_}@5VoQZfjERH^gHD1M}E(R4P_q|ol3+(iFG?F zoG7zERALMrZl%TqQPLp@@#$&Y{LLAbizuZcu2WM^-zc!P6y*h?#EM^*rm<04g4u<# z>KJ5s_l84I2l#*7USLy~%GCnl|B<^2Q5PlYREsvhPUPbEQTIoY=S*JbSf5T%9%DOD-a*R~fteuu045mho#D(X~Na%C-*602oi zp*0k@ZmwdVLs*REl@ehcaJ zY(;JrgmWnUFMs8rYCKFBEDZL3450!1At4n%^=^)oD3xrtV5rHQ~JxSW!g z3oYpB5Gv4P`Jf(MtPjB|A(Z)q)MEZ2KI;*#JL@C^@2ZVT4mlQyZHqiH_8y0@twVw0 z-~g&zBIHGG`;#k1Cv+s28(7s`Z^me=daXma(ndaaaKe+x^d4^PQ)8wyZDQCgi87_e z)}y^-^#Ry~zrxsKA4q?NW!>!j^JoQUA?>~0k2 zWf&>YK<$K+?X_Vr;Jya3GlW^uK)qi9xx(6p>D7EFb-a@rx}JmwIs!5K8W=wc#wrbKUfVB_twOCd zURPieJ=>2WA0C`Zi0x?4lyE)-j+(u7&4ERY0dTS~x@|SXM6(bXDAe1|OIsoYG8NbP zyn6DU#iMF$(iYgmFh4R3rCuUgAqLk#J|wqu^*1p-l|2lVb809yw;rnN7iMscPfxNe zZ9AM7@vj1VE`B}ZUu`0L8Su-oGkFPV$T8&2A^hevS^g>60(&{Nqjo469ZCanQ2Og| zc8ZG^m3SvmhfpBFjsUhV0LznLrvZB>0Ly6)!J!zsp9A=006bAb-3Qo(08ESJAi?}M zpqB&CI3M(9KyL=1ky8J9!1@9(MuPnam@>*QrU=VoQ=A7dDgeaxFWA@tO9{YkN-))q z0=qr{`%Z$518i;pW|d&cfGrQeK9^u=fUOR|&PlK-fR$MSzz-zgT>w@DVAT?ACSZF5 zu(u?b0kFdX*ggq1AF!GL>_rLoIAG@junGyb1Td=)Wn}21p5ZClmKk51Un1Z!~kptfm!%J1DF#4>LtV%0Gk_tjhA3g z0=75+OORltfRzMbF%oPcVAxdfxnh_EBg$3!U?k?q5R+lrr(Pl>_6vOyFdV?c0mKdo zMx3TL0Bez8Ujo(?fPD*CE?hIYd<)|0d2af1sq_JE#d9O)TU;j2TtqyMo1Z` zgh4@}L+J15c}>T#xmQW-QNC2XKiMOk5@n1CTrBPv-sL`eZrs=^ADuIxBl_r^Y$a5R zx!&hS(iNO)Tk?d(Jse0R7}-)77_)9txcXb4dGz=j_^$YKGx1wFv z>KymdZlY?>>*{=ao=!4iWc#sSpt`cKE1ZfwERXO#)(^7Wle-4|AX1)*BtIy?A1RpB zF2efccZhC5!!=iou;lu-NZmq=r_kP}g`<)KJ89=q4XjYydW))rEH12jk86p?wV6^@f3V{3R6l76Qd$AkIKUiZ_#vRx}(+M&g#> zxbKll;G`XR`0Y`XJKek(1iwe{TnHj~(w%`nL_+a5a8p@1r@$Up6ihcY8Ry5K+!9k@ zUr$fxYv@m+3VKbj6h$2~z!GP~cq3+>h9$_dU8U-A~%6EaPgyJs0L6+dOdo^9a0{gnpGwdDh2-+SA}B@<>fORJpchDiLw>;_H|Lk8OVcW` z3JdKBJOSF3fb8DVVC~$(G>BQT0LNO;iwP$OVOQlJF%Zi#n6F9;ZIwAhY)Th~yXwFS zBXr~48r;P$*#v_o?H~njtOpqv<(Pj9%`Jp;^v)5Mhc?NB;1(P>o?lANl0&9Snskwj z6=?00u7T$Mauqiv{+poJTtqM;C=rqfhetsK^b1CgfZf6uILN0KuZ3LKx5K+F*xZ)| z^Vh0w)ZsHE0S-Zc0rB2*$zuI}kXVNyv?$qkoS6j6G5127l1^t8l4lWye{_+fZDLg! z<2W#Mw7)jSl~T9xJc#V}5m^Vxb`!~jCxKWHKqPo;ffqSvA#UUoa>VGeYd-?`o~(to z3wOYa0S^QI%{$;ML<-YF&}ed{IuMXE0ghrIT{OrCpn4FA%Y!I`ps579a-pqk5Hudp zs8aHWzd(U5uJzcW1(#yaLFK0lgO_c+=2r9EZIwlE^%hJ@T_a0?PRpPx+8|` zBgg#sVT9y*_+i8zMKH0;0b<1M+`UF^jYE}~_UEVg9_7?`KRkiG^&$SpjL`xCJ~>G3xX=D39oXHg{haNNS<&LuxD zwABVgSuse=n(GT~VxXpk0_O{OkCWeHp`*dMsTHM9soz-Bxj*k3VKE2Z z)1A^CWZ~v<1Y`i3T1{SK@sDKh&9y_W^vbBW4i3;BbPT z&fkBH(CfGf9m{AxgI33L=ny~`D(R4BQ%J2U}x70uR;)3u6WUG$&Y9f0>T)LEFci{hmtvAHQl{duY z`$U`pQOQa+qm0sRu%hgq0o(}rTp_GlB!4zXH`7YKv0Ugr)6l$5N_Po=$Z9bwVja9< z-^d{>Fg|Nm<9!Ba**9*wt6g=FOt<8RDOX*gI6TTho30~ND!UG#`0-D-`!IC-GK;0` z;u4a$Z}w&0CmojHIE+X{?^PR= zz7-d_b^N+h&CbNO$3jHzR5ru6YoNsKw-@mTp@>qISpE?V>^tBe7qx%+(Ch8BJ-L5K z?+G6w3g2>1?!TM9#U0;2W;*#ZYVv7=OGrdyIK!_|85bM>lt*7jDD7Nmi^cD{XZW=E#zA0XaI!@x zt_F4+C>{gF%Rv-Bb#QxLPoZv;JngZ&~PV?@UoR*H+;SNq)PtWi!*6$5g}* z5$WM?&$Bh>VNW=p6@;&Q6^UCU1*+x|y0O?XBLhI_Mmgfibq?;=s^hr@z-zRxdtVS< z?Hz26qy6hzasI9))Mfyy@;gDO08X%iulI95yRy=Opspukh#(x{ZXB3S*K(r|riP~X5S-oIoP$~P1KiUG zH7R91#4ydn_T=#-se|{3iEAxq-t4-WB9q{!9-feT{}1-Wrl=)FVGFPT~lAl z3zN_dw%t?x=ue8=_(M~N-Rv2V;QJo#`vc>-J%?iM>gp!!W)AAV+YsD;f_OtrIGktW z7X00|Anh*Lq=x&~q12Qnsi(8z|Kscw99}Nr&GcdGgE&9t=REB098kiY9dLhZ5RG8& zPNCKd?iJuZAKWJh*b!}fb3#r&`mMO;=RDhf_yjT1fYeT5r}wp6#HaFXQJZB;IE81t zT;f|sOGg(O_}`omP|61nyi;JkMYqPFoTs#c@U4V(23QHe8W?(~kmWrGEL_qA)&*cy z!961Gz|nZKfJ35A5I&G7Jpf0ucY&WJ5GGDxnD-%g+F}XqEeXvHhBtv0%+Tra2E^JU zVVwt-l@JS})#hmoV3h;w!P zhyPt4*Dq^~XO@+&+PGoEqpP;6CvBu2U1i>=PF=0OS3PCx6J=B8&da-R{)&YKD;6&* zUP@^Aamn|2#l?$?XQ-c8vt`wa#~nDA)DxB;4K8IdkKk0`d%BZc!p*o%tJbUnPMgJMWmHbC3 zJ*)on23=L}{?B_M!wPVySB7UFp3@EWA9n0gU@hmG&e@l;d0Z5sHe- c;$H4aR7rm#m#gbZR#kmNQ&sg>do1+-0$MNoY5)KL diff --git a/misc.c b/misc.c index 46a752f..9780282 100644 --- a/misc.c +++ b/misc.c @@ -34,45 +34,6 @@ static pthread_mutex_t swrite_lock; extern int MIDI_DEBUG; extern enum ASCIITRANS TCPAsciiTrans; -static char * athelp[] = -{ - "AT - Attention", - "ATBAUD# - Set baud rate", - "ATBAUD - Show baud rate menu", - "ATDIR - Show dialing MidiLink.DIR", - "ATDT - Dial 'ATDT192.168.1.131:23'", - "ATHELP - Show valid AT Comamnds", - "ATINI - Show MidiLink.INI", - "ATIP - Show IP address", - "ATMID1 - Switch synth to FluidSynth", - "ATMID2 - Switch synth to MUNT", - "ATMID - Play MIDI file", - "ATMIDSF - Select FluidSynth SoundFont", - "ATMID! - Stop currently playing MIDI", - "ATM0 - Disable modem sounds", - "ATM1 - Enable modem sounds", - "ATM###%% - Set modem volume [0-100%%]", - "ATMP3 - Play MP3 file", - "ATMP3! - Stop playing MP3 File", - "ATROWS - Do terminal row test", - "ATROWS## - Set number of terminal rows", - "ATRZ - Receive a file using Zmodem", - "ATSZ - Send a file via Zmodem", - "ATTEL0 - Disable telnet negotiation", - "ATTEL1 - Enable telnet negotiation", - "ATTRANS# - Set ASCII translation", - "ATQ0 - Verbose result codes", - "ATQ1 - Suppress result codes", - "ATVER - Show MidiLink version", - "ATZ - Reset modem", - "AT&D0 - DTR mode normal", - "AT&D2 - DTR drop causes hangup", - "AT&K0 - Disable flow control", - "AT&K3 - RTS/CTS flow control", - "AT&K4 - XON/XOFF flow control", - "+++ATH - Hang-up", - NULL -}; /////////////////////////////////////////////////////////////////////////////////////// // @@ -434,6 +395,7 @@ int misc_check_module_loaded (char * modName) } } +/* /////////////////////////////////////////////////////////////////////////////////////// // // int misc_get_midi_port(char * descr) @@ -477,6 +439,7 @@ int misc_get_midi_port(char * descr) return FALSE; } } +*/ /////////////////////////////////////////////////////////////////////////////////////// // @@ -842,27 +805,6 @@ void misc_do_rowcheck(int fdSerial, int rows, int * rowcount, char * c) } } -/////////////////////////////////////////////////////////////////////////////////////// -// -// void misc_show_at_commands(int fdSerial, int rows) -// -void misc_show_at_commands(int fdSerial, int rows) -{ - int index = 0; - int rowcount = 0; - char c = (char) 0x00; - - while(athelp[index] != NULL && c != 'Q') - { - misc_swrite(fdSerial, "\r"); - if (rowcount != 0 || index == 0) //rowcount not reset - misc_swrite(fdSerial, "\n"); - misc_swrite(fdSerial, athelp[index]); - index++; - misc_do_rowcheck(fdSerial, rows, &rowcount, &c); - } -} - /////////////////////////////////////////////////////////////////////////////////////// // // BOOL misc_do_pipe2(int fdSerial, char * command) diff --git a/misc.h b/misc.h index 3f27750..0118513 100644 --- a/misc.h +++ b/misc.h @@ -1,45 +1,43 @@ -enum SOFTSYNTH { MUNT, FluidSynth}; +enum SOFTSYNTH { MUNT, FluidSynth}; enum ASCIITRANS { AsciiNoTrans, AsciiToPetskii, AsciiToAtascii}; -int misc_check_args_option (int argc, char *argv[], char * option); +int misc_check_args_option (int argc, char *argv[], char * option); char * misc_trans_to_str(enum ASCIITRANS mode); -int misc_str_to_trans(char * str); -void misc_str_to_upper(char *str); -int misc_check_file (char * fileName); -int misc_check_device (char * deviceName); -int misc_set_priority(int priority); -int misc_ipaddr_is_multicast(char * ipAddr); -void misc_print(int priority, const char* format, ... ); -void misc_swrite(int fdSerial, const char* format, ... ); -void misc_swrite_no_trans(int fdSerial, const char* format, ... ); -int misc_is_ip_addr(char *ipAddr); -int misc_hostname_to_ip(char * hostname , char* ipAddr); -int misc_get_ipaddr(char * interface, char * buf); -int misc_is_number(char *testStr); -void misc_show_atdt(int fdSerial); -void misc_show_atip(int fdSerial); -long misc_get_timeval_diff(struct timeval * start, struct timeval * stop); -int misc_check_module_loaded (char * modName); -int misc_get_midi_port(char * descr); -int misc_list_files(char * path, int fdSerial, int rows, char * fileName, int * DIR); -void misc_d_type_to_str(unsigned char type, char * buf); -int misc_file_to_serial(int fdSerial, char * fileName, int rows); -int misc_count_str_chr(char * str, char chr); -int misc_MT32_LCD(char * MT32Message, char * buf); -void misc_show_at_commands(int fdSerial, int rows); -void misc_do_rowcheck(int fdSerial, int rows, int * rowcount, char * c); -int misc_get_core_name(char * buf, int maxBuf); -char misc_replace_char(char * str, int strLen, char old, char new); -int misc_do_pipe(int fdSerial, char * path, char * command, - char * arg1, - char * arg2, - char * arg3, - char * arg4, - char * arg5); +int misc_str_to_trans(char * str); +void misc_str_to_upper(char *str); +int misc_check_file (char * fileName); +int misc_check_device (char * deviceName); +int misc_set_priority(int priority); +int misc_ipaddr_is_multicast(char * ipAddr); +void misc_print(int priority, const char* format, ... ); +void misc_swrite(int fdSerial, const char* format, ... ); +void misc_swrite_no_trans(int fdSerial, const char* format, ... ); +int misc_is_ip_addr(char *ipAddr); +int misc_hostname_to_ip(char * hostname , char* ipAddr); +int misc_get_ipaddr(char * interface, char * buf); +int misc_is_number(char *testStr); +void misc_show_atdt(int fdSerial); +void misc_show_atip(int fdSerial); +long misc_get_timeval_diff(struct timeval * start, struct timeval * stop); +int misc_check_module_loaded (char * modName); +int misc_list_files(char * path, int fdSerial, int rows, char * fileName, int * DIR); +void misc_d_type_to_str(unsigned char type, char * buf); +int misc_file_to_serial(int fdSerial, char * fileName, int rows); +int misc_count_str_chr(char * str, char chr); +int misc_MT32_LCD(char * MT32Message, char * buf); +void misc_do_rowcheck(int fdSerial, int rows, int * rowcount, char * c); +int misc_get_core_name(char * buf, int maxBuf); +char misc_replace_char(char * str, int strLen, char old, char new); +int misc_do_pipe(int fdSerial, char * path, char * command, + char * arg1, + char * arg2, + char * arg3, + char * arg4, + char * arg5); char * misc_hayes_flow_to_str(int flow); char * misc_hayes_DTR_to_str(int dtr); char * misc_hayes_ATQ_to_str(int dtr); char * misc_get_clrScr(); - +//int misc_get_midi_port(char * descr); #define TRUE 1 #define FALSE 0 diff --git a/mlinkutil b/mlinkutil index 317b421ea683ef06ceca64142f7f0a813e9cf684..4c8fcffbd166698975b21742eaff14929323cad2 100755 GIT binary patch delta 4054 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 Date: Tue, 11 Aug 2020 18:27:10 -0400 Subject: [PATCH 4/4] fix ini var --- ini.c | 2 +- midilink | Bin 61528 -> 61528 bytes modem.c | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ini.c b/ini.c index 78c6107..2608694 100644 --- a/ini.c +++ b/ini.c @@ -321,7 +321,7 @@ void ini_print_settings(int p) misc_print(p, " - FSYNTH_VOLUME --> %d%c\n", fsynthVolume, '%'); else misc_print(p, " - FSYNTH_VOLUME --> Default (don't set)\n"); - if(fsynthVolume != -1) + if(modemVolume != -1) misc_print(p, " - MODEM_VOLUME --> %d%c\n", modemVolume, '%'); else misc_print(p, " - MODEM_VOLUME --> Default (don't set)\n"); diff --git a/midilink b/midilink index 2d3bd2f775f4e1081effcee114e56fe0700af36b..9463b36e0690ddae77909a6734103ff9ccc05fb8 100755 GIT binary patch delta 10589 zcmZ`K!~EmNPP~6&_b(s(eler7;~&#^ z-gPvbLPauEfW=Xn;Vr>g^m!H`3A5|*Fr{Cel&O8JV~g?Rs5t3KyTQ_e*F+_ai%ta@ zO2W8$iqGoU@;M1)j3||sFqe8Lbq;fOla8(Qp?=yEbW)=F+M1E7W2^D4sBqbKAMOXh z_49BK;Na+R*-;Lc?a$WR`ccDxNVgf8pFGzGZ+9&R*l zjt)m^{B!hXbQM>`WTN4CcT9rFituMKWq}g0j#cOC*qc~BY@yVu)UjvqwqePr1vd># z7uiLa9riM6z$UpCW#9(+8gu|BE27XDJV`N5UaO@P#CKxmffFfQTy>YNO=nq=r?VJy zb(RVEJ;hQ%r3|MjLqTo6GPz%9sgAueMQ5>Uv0~Uzyi=+35wr7R!Lj+$;!>R@13wqr z5O@U!xUtDpDWGJMYI)=t^-TKx0%@mTpoD3#0*nAq%FE%OR3kvhp_&gk=M7iEyf+Vz zM;GwP;mSZoo{qf;wkdpl_%@H(rEz4!@5F_pJNR_meDF}rhy+xP^G775wDK!2W@;TR zXZKihG&;65jY^u;qO(XeBnHU*f6|MYd$vKxbe1f9a6}kt#UH`=R(xwjIQ;u~L|jOV z((9?iHqel+vvgriybMji`uG^M6EBEYN?X$is%_9W;A8PhRENKhkAm;c_?Y#YMa?sUe-I^RX4rc3u*yR# z0-Ds59W@@>1fY2{verWz12mCG?p+Tp6=>evtjGJ4!jsN>IFYsp;K*rr&bN4|gF)V# zr#JDfB&C$B1cm)aCLjiDM#>R|OGd_^EWC7NN#L48hib&peRjLg?H6N z%x>rTHYaz!i!wU7@_T$_v5Pd+b`@U{NhBbJh}r1ur*LvZyMXVBP??odA8v8W*9n`{##lqfV-VWFb_^oPsq@^ zgG9vaaOhY=4haA!_g#mMohxuQ0;dWuOkWtbRV%8aFoCP019eVrRVO$Rcc!b7Gqgdc z30*={04~8t*9(1gMf1AGW@y5kqih=W`wIA2?)NklN4|x z)v-C#M9dfV$O0WJ!JQd02wrLCXmk^g&rE^kUz!;fbpS@X^%6`XX0e^OtV8KQ2Mje3GNgwKtV!4m#B>N&Z)e0wU4AQ|F?giErB#r&ctz2au2f7q9#nw!;!{FfD;G6y81hG^)Xc zV@5%(er3#9*h@Yg6Ozs)bW?j!j)Uv@L}$tFBRd;p%RI9C`pDh^*+qS1 zCxI-SImX6>r)TCn@ad38~Iol-R7n}t6AYOBkrDh_Lh51Mw9DSdH z-TZr-&Jt?!`k}|#i}uO;#HN>XA2`(>Y>D@i{&fAM^kD!cVh;DzpVV1)nxMz;k|fuE z2HB%#o}}O*I5_G7nRXUTJFrP-Ia5i#2^1-F@Dr*z>vR!IWz9(?_;UdN1Ob1JfWM012lzQ6FZ@H@Uie2E z4VF_>FZ|q3e4$t7SDqd~6Zq0Wg7ZrVetdbHYnGNibZr`|XVa zJL4a{aKhJ8E695@BZ9Ydu#crn!1r1T=wqq5*Ai#Hx5pZg8g)`$0QqmNJWB$Rx2tBh z+LK}8cTA~9GIhkTE+zDLn!nCs}9Go(NL@U-# zdJi_$wn?+$9G?JdJ7(=%j!&&c3`a@;e#M}aU2u-R+nWb&-M;|03^yCX z5_&W3BGA0LBz<(9<8?U<^4abjlQ6G1uUn~9l4FT*m+|68%-cjSpAUBKCn&E@ZdM<> zn|*A2=~J>0vzE8fs?sY#9j!|vo-jG!aQWe_Y0o~Z1$^GN_+#)->D7wPMZ9nJU$GpXrV)lU5qj>z}`R0*Y zh)C(zIyGM&D5l+|XD4XAy<0A|kz!gLdUi#wo~?4z`G{80-*$*5eA_`yKun}f&u%3c zAPO(SJnh_fUA8-Jo8G;LVp51re(wQEL}oSP{qD&47rPz1nP0o+i*$+t*>#3zg8LRwJF*Ff_wxL>DxxuWrd;wT90{v}Zm*zqMM zv;O{)X>FgrH~Tuy1-3qf3j6cjVUN!)P|N?QcDBN}U_ zjGFEZGm3FKdaaJR=eA10Rl$42qL?*K5(H`DR>VkqP^$US(W5#@ z#^x#Uqk2QB*+;j%!>ya?fuuJuq*_Mw{&Wu zw0H;vD4sqwQ(iU%(v1`^UZyw%zB~o|P@A5u!yit~gT#0+H7=}}lzKiKBaHLf-016Y z!nAlel$J~jm)R@Co>FV$ES{3xRIO*T@VaRUaoMH_W19QS!5G2!Wu`RRz0FX}6`PCO zbyg4WKi^L?qf|V3I(d{>Fg*_Duxa{6uRlF5ctA-kZ&poH%#0qrB^6(PdMvykg+DV6&hb;9 znGRL?*fW#SqXx$_320#1T=0OzpMI^L6w~zQ@&_v_+>dhkYDTXYS89skh9)C9{*PI0sy~S?Ai=L-bqRBFfEf;zpL$5~YH8>Jz_gU1%9c!YR4QxHC3>Ls*` zJM0iCsn`J8##uWI>?J&TZc?5b&9Jk6fJVZjxg;Wp03%f&!0tF)R}F zd$@7;(;D0{9P(;C-i7^t7 zF%E)b+9YEXtft%;6=UbKG{P8AUYSod7g;h&46FolPlU*ge48;!mObKNJ-E+LN=MU} zmg3jwUG0M@18cya8T$50CQSS5AeM@@^rhxaz^l8MsLV2C2TWyxxDO{{^fg`((QdnfmXOHEsw zBoSsK=2b1sRuMi&_Q%%zJa~!kt{!H zVB7mh!AM^zyl&~#uqur^k0&#(QBH26>wX)FlFp^^VHY&+8>gES@A@7%**LyZ3r<~7 zSsFId^T-1HC!O36P8Rrc@#RVr#6zA*6?H)?rei&Y&&mDSsi23{S2k5brnTdBrm=7~ zY%&!>3GBa221_2dY$lxB&C4|C6h65u3b@}cOMufIw=9`(msf;HO1$yq(H99M)LjYrnyM7e++j!7F3nRGz$&oPG*dw#my( zBHH~NLn%g<7g@^ZE-8obk~p~_$bc2NbX6YQ1$M7m1L%TRZ$SI-TdQZo*7E!61PCj6 zRd_-Kr6kc!^>9yhkdytPbq0%Cpk?s1GeFD2v#Y|A3IxtDp0g7;GXz>_haZ1C_!+21 zyuT`JT$R9cwiE3Qz^M^vk9gWGpw$YrdpwN++PnB+RmiwA0_P{5BVBK>To7oNcv=k5 zt_rlzc$x}mU0Ay&WSmvt9OpSHz>x%bXZNfwqjNl>%)pz6Baq2%K3wXEks(3bZnw<^o!+K-2NGdqAthnm>Ub1kRH@ z=Nxd(3$&3u?I6%D3A9+Awh3rg@jB3ON8k+NITgTpB+w*0tq^Duh1)lext<4P%|-() z0$&CVYJu|`&j|!hhCplOX=F7g2(%w~+P6R}!^)S|K;-Rx=>hz^Wo-$%(r{y~RD|X> z+gO&s5-dsokf;?{5>wxNF`Ft5wLef>E2l!+HYQO{6$pWWGQ%v#(gQXIutP3|!gWf>sUC_j-hz%nD8pvBiV1`=H zp$Tqpg#I$n_o*Q6|3F^`^ja7#5=R=Rg1#GoX@<5}-yGmeUXmx7l6Mm+p_DqJxjzjO zY2bRFu?{X8G*+iWG7_rTOy=yKU63;oJeu==By(y?ydIT;xPkXKc{_1)TT2r8qbu>r z?~Wv%9=*PZEeEy3D~Q@&t#=(omg-6H#y}3BfqJ$ehj@E5}tURc-=z$=9dCi*=Y2QUMBJ8XQrGw1Yc?(S{H+Q{nA@;3#6to`ms! zY5YXGNZ5I9DGqLr2k5nR{~hRS4g!n-B_IhnXkr84^Wt&`$0~xW_(j0gx(;5_&D>`W zH?Twj*gVG=9>6{T&C8B^;;-=BXM;B8`ugLRJm3WD%%j@Ap-mm5y8EFpf5v8NgFUIlJLh(M@YN8rUFcu4ks2|cyK@W@vcfC%ovrOI z?1d(8|4ZA--wc)@q5p|(4)lBF|87f!ey{v-+mJr;f3?{_U!zz5T<$NnJ1|EH@*3D> zdkF>z0^A$6c<8Uydik%~VnIQ>#@m0{#@>OSZwUNVJb$OaUt;^A;hjzYhv0W0J2u}& zKQ*j;O&ZQt@Gl^(Qut}a@QaCj>v`)pI=r0Keb~9@RV?e9LNqYY-(q=sS=O3 zeAOo1o&{XCL%J8yaj{tt(Kqb=AM}GSD>v+(|K2Ew>OGes|DWgcrFp;|MAlo<)K zbC>atJJU7ZSyGN-n^ZdWIBgHi^{jA1)v$u0b{Qg@hno}AH zly+j7ysS;KE9kLsi!Ee#YSQQ=&zV~Sug%%^1e#3gPuPD~#lR~a_1!sJ61xNYTe z%3Y(-ulU$5+309E$tUvK?A#AFQGHWazbyoj48~HiR_;){c-X&xv2yM0g>0+FeF~?* zZXN=8*Ud3SjJn;d4Dcyg(TWg$ku`0F&@2}ZGgd&9u)c0hYBv3mfZg_q^pQD*A{@v!sk>(#`{JGuT)9ip{v+yo8N zL#}RNH~c#TBooSUptZLt<&c6xQX5iJ&{&4nHp)vTttuuTTD zBl^-nYU3{)R|PV@^V8#7Z!Z|U!sJg!(*AX1{W$I@p1DUgn1*xSd3OGKO6gzAarJo1 zo{0kwJOB^uRd@I6&vAS3jXk5`uNmQcfU`yS_JGmBvdy1=3pf)wCVC0lc)9tx5ArC*Zh! z>4RFkd+qgYkHddjdDV6F({k z#?8L&H9X#O;5hM+FX&O$w|*lLmCFV#^ZFqf$NWWa{&yEy^XbPqZp3}xHMem+u9Dwm zG`zRPa`1*sq&^0zP>||fbsN{_Y6PhqUaAqKY9TU2e~|gnl@2l_-#G3Fujy%svF)htL=g4C(&BYxw! zzk}2xAti0xW|wR!gQ%{%99Q=T*4hU1{!c~dMXWs(hCaj74=JU`vk`Tm;m?Pb_cvRQ z!{4_*LR2d>JG9|v5M_W?32i&HqtLEEdju`$ETXcYl|!qBb^zK%Xm_9md<SG$SelE1Olp z6!cm?bGHciRy0g){y7X)H6;J|k3d{34#1Co%|dG%lK<6T614Fcz=2i?Z67pTaytZ< z-ACtF3ly#n$%IPZUtp=Nl$3lI=V9C3y**`Qx^^zjN+6zkBYv z=iPgy<(9bRmbf-2H0>vfO8W^>XDsrA*yZ16zhC#wAHlI!x9UygE)ZdXN@$ zC<){4Eh?KQ2*yYs=Ca*iHC$T(qn%K>9IA zgS^s%I3hk;b|OH!6{N^34a55QIMg4%7%!9E3eZ^qI+Rz3!CSyW{4zhMNgxgON(bTQ z_-LfX*W)*%Teu=28>Qi$2`M5f0)Lq>IW$acV6|fnY&(_@Tqvb<2KGGOGB6#r;HH6@ zB1u2Y4tx!LhAnbE8igC>tIdSC#|J4x)p-W?3iuAezYf~sb-Of)On7fnH0s1BljcE$5(cNBO?doZO-3uf@?xgW zdFAwOTP_5sbtt8I>59P`rXx8(=Kr%%%-pv>bJSqX#qSS}Laq2S7~hI-4~~ZaJ{_DC z*`oAC>H|Ai$TV2Hur67KOxT#5fa>x5WTmuqC}Fh)`jhd|WF>0AKP1P&?+3{V$%BT8 zl;9~ihjww+N0eGoex8k72bt()_l2YQIP`FaItmTKc^2bSoGXlgsr6@lIxVBx$naka1i z8sDEK^k3oo2Za8TZX??gA@b(Tyzc*jY&nAemXZN0bUmdM&B2<~Y3U6SzF0kZ>gM*I zHdrSkAJYDmV%E8rGQB7__wE_UHGC{pg|6X$rY1qWp2C0CSgwhiTM6+&l$a^A`}bju z7b^lx>fIf+UMwFl-;Uhx#YO-o^6DM&Vp_m_d-D|Dr-{~F^h#pwF(8rCo;}~HtV@%l5L}X$fO7HDw35)Z2N3ln7E$ByKhpM~ZFp|FJoKOj zVw`GVFXDI7<>&-Hnm!G3RXSu`)HY?7Nt{?@ZBZLozt+qlDWJ7^2+_JdWJ*vMjHfd3 zsEi`m3~yzm0DLkdMLK8%?0bACqck)o)3>+AXf+^RsLczy1X5}^KB0XX2F2;(DtxOZ zVs^UDx4F3Uos`+dmEY$xi(R3kYE^tiB$6et>Cr`|6Nxa+7du28jl!CD#yg3c|lsEcQ)1g`*cV{T!+&42bWt85N zBqC<3)4)#7B?;i-zV9%wiv-CAkksIXnG2(~=|u-AOz5iUP=kwm}S;3_r+{LGntohog2pCOZR`e`$79+(8)Wu}e6Kn8gla|46%mEynw@ zqeuEJPwQd%0$46`KwwL**;*B3Y=luwmMF-@t+=nI4QxGrlD#yO(vc0F$$tVxFkiQw zN)P)DQf_ckKGE)PLmC}TB&_|&SnevY$N1T4L0TxIlnyh`#>D-n>LEc@HEgxqQ#-)T z4lu~q8Dj1wJ2ixNgwGC>!4m#7>_xe!e0wX5pc(0-giCUW=E59o>`{-lk?9eeNBMe_ z>a8~-<|DqpK!xu;7v>{%a`ZzA zzUDvJ4c2&zFAm){Kig*?5}!WFLy&Cpa!Y-v`O~k*rB6aA5%WQJ!*PSP-U2=TD@peH zbzVc)kP3TLX5rSVr_~WaiWz+nD9v#5-Po05OwE?5!(_;60@E*vXxSU^(J2VTuQ`+RZ{qrizEo@ps2Tz z&Bw={yAc!~Vq_)3_}wups=F+(5GWRMyV|BTldm<&c(O-ULH}M`(7q}&KW=eAa zPDJ_MoIs4s?a$Y#56FW24teIABw|8A?#q4pfq#;SakzwR!fSJ*s?R}Al4Q8#2-*_| ze#UgYd@Rt@P0;u4j2Pb2`#n5e0=dsqNDoiVeovg^{%%`{Htx8*0QSGF@-#_A-mjXe zb)>_@J1tr>nL1)vw-WmGmM8!Ty;ExiPEBS9PI>h~bD2BSO`6!fG# za;h7jmyAx~rFqea!5i~N!|CPgypK>9UYKv}+XY`wkDKdI&cpH7`Erzn@8&n64S3hM zk?1D=Xx6vLPc|5E(ij#*l>e-(Mjkc zenP(wUhw#8*&3xZk$BP44o>kHK6Gk|v8^+*4AvCL=QZeq1gXR;wXZj_SM>f-wnv^( zvo=6&3&1MZKY>t`2Gb&K5!iVelUg8leM@MbdAQjYUcR61-Jf8*hee#rg>! z!AG@i!b~`ak2hqZF#M{)BKPO%IllUlQ4Y@bknC6ZDC6G7I9RP@V?uhJ4(%lVW6hZ~ zRXg>dgZuokk-esydWfJ17JtK2f?jxNgxYE`M#0B>qfvzl@L}V0bOB3E86`e%q)Z`Z zfY+l)>hW3(wbsSm2q;&Gk${dTXB}K_K&?d#M@j&G-K3OVbPd1j-v=JszkqZGZZ<`w z_;=bxzVP8s5jNg>jLd{Ul*bE~d4Jy%1?+Yw^d0d8iUUESwF|E-8}3x7AW4hp&^T zwo=S}p^7-T!JT68d#u}EMZn4cifQpy5>b#Rp8Yx@Q^{0*{$EuR2Q) z87ckNpyta1#k9MPtV!?d-FB%}vNax@&uA5WxkHqHxr53_Osw6=ZX+BZ z3opVv9o+Yw_EwKi-?xWiGKf$9+XI@2%&P3+)L8i6%MaGvBg%)owTEJ2J@kq-P-qqw zCn-0;70-82p_mmQjTeG^n-oZVyjU)53yS#`FyDd)J@72oApE#E4idX}NgO10a)|}4 zz`IH`a83TYWLP$(A8?q^de$)YAm~RqxLFYL5WQz1NQjVvVg|cNINpcSrhsq^!n#Sr zruov0Vq8wY*Ae$UUPIuj;Ag(tMUvo@&AU;Hi42hg_C;$PAHuSC-=prad4U5T$&EE{;YI36p|;U(K0i9 z0!3mrKE}J?pE=#!!!t(qZLFM}4)LBiIU3?KZ*q0-8Bm~+D8_7rOU;wXkx;y)QwpVJ z{UAZ{v?bVcltscXZvj8hW@H=i;VF5rF&N#^TW{CX^%TU_$nW30#b~m@< zv=QEaewbQ~a`41yFxX1+MSue!qXI7cz)snME`H@|4ZK;s{sw+O-5_2_Xy+WlrGAPt-xG>Zt#E6bvk{z`|+4rItYbj)^vDG z`fgSxT=U(t=1WX09J3b9rZWbSWfaR#OY7{MxkF5wST#zdwS1iH+&VbHgf=iuIVVkQ z7BWILzdc(vB_DYzqMQ4|X=2Sn?+o;6g8`27a393-XV zX-rG;oAi$MzLbfbjK4A~Q8>P9PKWp2$oUz1ejLdbk&F(-p=Pi!n{X?#I~ydBP93pCo=9~E^eIrVH-)J2TPNqF6lfMQja9r{R2qyadM>| zf;zvlG%Cyc1_SbEUEGf@7UYZY)k+JbOP)m)cS$d%6TNlO#r@f(p!+pcHdVq#Wbj(c zNH{t+Sqh;-_Fg7~gL~4l>2R#CUZz9m@bP7FAicaS1rC4QvUDO{UJ)5q;!8gl*X_Ik z-oo&%=KV9 z0Cr2jzT`0#U|m?hI&w4>?wjE!yd(o8VFLCck7WZEFJQZQY$RZ5cq>>KB}m@jCF4O- zAYf~FY#v}`0=A6DN&#DhZ-a$3g5)J$vI-<`3)o~Ha|5$q& z<&NO50bZ|yyyx*Ewl0a_WEAMZqu3Hxs<{~K{SKVU^?rM^TpBaVq?q5<6|s^$ll6q( zZZ#N4QaQO2Fnz0^$r@7PCtk!BXFIukkn9KY>Hvwy-7uK!K`s2IT-->T$$Cm@WUKf< z0VOe|j%n^qgT_z@e!y5K7Y7z=GGRLcTJ1SGd*;^{_*1F5{~~**w!{}x38QUl|S2(sB}3sJRT3TD+jY_6+ns9aBtLFokiA~3GfI(PN<Fz{o|2yx^PQsJ$CY-?`$tN+P z>!az(mX(BN@dBWEb1iv!hXWW-GgY8D$LXP04+Q-5raOUFHH3R}4_-yU+Qx@F!A#5@ zBIYcqw3weWiOXmR=q6Zak=Ubg6f`ba#I)aG8raz6wXqP0ZQ_Zo1ikhidNI!GA~r0q zkj*6-9I$3#VBqb{Lbm)5I0bkJ=-2!K?{XAbLlO(w{R9{GT?e4yqC)mU5A=6O5gRkM zkZmVuwV?b@0?#UB!y*DrTm&?{g8a)lpa|xv>O&HbPzA$U&?SpNrIR-;CwCkiW|e{_ zoXVV>+F8gJdrx>IZU?{?rS~Qxyowb^_Ayucja3T9ND?}^x4;-RiI9n`qBIoqPE8?O z8{tc?H6Xn>)+c@CN+EkwkSzgOtst8OGPO{l%7MfTerZVknpFK;A-h5lPJ)3XG#7cq z6G$gF9?%e7Pu_moo!-$@x`(zF9GHhf9CaS5grbv6;{WN#gEY0<@|;{eyhS^d)xP;e zIZE#qvL=B_pB_Aq+$?0b2{O0+a9bfu34PAK5&9>D`9HQ>+Ta^i;+K9v@qk+1D5@0qMILchsA3;KTjllD~T`}L37 z`}NTO$ZiLF7X8yCN6425&_X`GO9rh`&RXiX)hUUCJ_ zHmR_vy%On(T{RUU2W{L6I8BYbLVo37H#TNwJ)qQ=uIlJw+QtoeaPjzXxDvX!>ree7 za$v+)R}~;n>nHxF-hCA+z-)fY zly8Go)aoYjg~q{M#+SEc%Hc8u7bmjK2GDX^0wd!)q;$dQk+&qk>>!qJk52fpojmnE zyb5Q1?wfW6O|Du^?aW%D@h&dio{>;@pHg4t(Vh6aKGY_x6fDS2`LV<-V2rD73eY`xY7TngDiaJoC>YkeWx z0G>v8J-u!xm<)7@k22712OsZ&k4nAY$1=QrXA(T)f4p;jpZo1Fr?aV#!eCCVczE3~ zdDmm{!1L@oHJ(`oL&+GV_soRw>%?2?;@}?*4%f*Oz5&hUJe#_4M6W?VmghnA&f~Va z#xc2|(QI9KjN{&T+;g7OyBD^}fMo&AaYI0B!!I1S0(1g-j>k84&F{O$5=_U^!3W7| zagYNgyH$P1!l~{&J8vDO46ft2BY6GpanIC0hMDY9KkC(+<90m8FVzpmj@|v>!qIn6 zQt-e>L~#usxhE49;iY?&aGBh?Co-c!Kc3Y>*@e>pkbeH@xwig?Lymm#L>DCacYWDd z7EcIy5*#v&{s%iDh2!G!Kldc7hCCtxdT#CXfqM3hpw2-Om*-b-zc9L*HeP*AQ zM}9&-;uq_OXWx2AMvm+;a!`+v*KNPq;`bB3#A7xDXFvX~J_#PSp43P7?djzZj@+y1 zQy$>udGL~rYxYVq7x13ms|n=T(^EO|R0E#IgQsMUOU0k_Dp>XyiH-||{#Kr0-y z{L5|U?zqo^RxYn~3bg7VC0w07b*{QIL5FN1j{7Tb>3K-5`=HfYE$kvY_qlrpWLGh- z_7`5w2@YHSVC=BlEzqjrwVFWd0x=d~tIpjlXe|P*6Tc3=$8n#6mP6Q6cJ6hzY$=1N z&bu7<;U7d*jjQ&3A&P$afvAbxErRLZMVsiYbg#nK@;UBvJn?{1`qxp2s%u<%V0rIq z>KLLfeuk)4Xp&R#*Mra|L#u*T2kiv3o6sE4VooC}7n&K`CTIttU4hmKE&L3kw9v|+ z)j<0Y+9haR&|=OaDhJvOXf@F4pq+r$au)TX9PmSN4pI4ymQVJDhh}{RHkuJN3d@>R z5h2iP`O?!A@LSzj*nB-oS~<;KjT&`7*+bEW#`OQ~EeU`7C{Te`1?@v