// // Audio interface Routine // Passes audio samples to/from the sound intemrface // As this is platform specific it also has the main() routine, which does // platform specific initialisation before calling ardopmain() // This is ALSASound.c for Linux // Windows Version is Waveout.c // Nucleo Version is NucleoSound.c #include #include #include #include #ifndef TEENSY #include #include #include #endif #define HANDLE int #include "ardopcommon.h" #define SHARECAPTURE // if defined capture device is opened and closed for each transission void gpioSetMode(unsigned gpio, unsigned mode); void gpioWrite(unsigned gpio, unsigned level); int WriteLog(char * msg, int Log); int _memicmp(unsigned char *a, unsigned char *b, int n); int stricmp(const unsigned char * pStr1, const unsigned char *pStr2); int gpioInitialise(void); HANDLE OpenCOMPort(VOID * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits); VOID COMSetDTR(HANDLE fd); VOID COMClearDTR(HANDLE fd); VOID COMSetRTS(HANDLE fd); VOID COMClearRTS(HANDLE fd); VOID RadioPTT(int PTTState); VOID SerialHostPoll(); VOID TCPHostPoll(); int CloseSoundCard(); int PackSamplesAndSend(short * input, int nSamples); void displayLevel(int max); BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite); VOID processargs(int argc, char * argv[]); int initdisplay(); extern BOOL blnDISCRepeating; extern BOOL UseKISS; // Enable Packet (KISS) interface extern char * CM108Device; extern int SampleNo; extern unsigned int pttOnTime; snd_pcm_uframes_t fpp; /* Frames per period. */ int dir; BOOL UseLeftRX = TRUE; BOOL UseRightRX = TRUE; BOOL UseLeftTX = TRUE; BOOL UseRightTX = TRUE; char LogDir[256] = ""; extern struct sockaddr HamlibAddr; // Dest for above extern int useHamLib; void Sleep(int mS) { usleep(mS * 1000); return; } // Windows and ALSA work with signed samples +- 32767 // STM32 and Teensy DAC uses unsigned 0 - 4095 short buffer[2][1200]; // Two Transfer/DMA buffers of 0.1 Sec short inbuffer[2][1200]; // Two Transfer/DMA buffers of 0.1 Sec BOOL Loopback = FALSE; //BOOL Loopback = TRUE; char CaptureDevice[80] = "ARDOP"; char PlaybackDevice[80] = "ARDOP"; char * CaptureDevices = CaptureDevice; char * PlaybackDevices = CaptureDevice; void InitSound(); int Ticks; int LastNow; extern int Number; // Number waiting to be sent snd_pcm_sframes_t MaxAvail; #include FILE *logfile[3] = {NULL, NULL, NULL}; char LogName[3][256] = {"ARDOPDebug", "ARDOPException", "ARDOPSession"}; #define DEBUGLOG 0 #define EXCEPTLOG 1 #define SESSIONLOG 2 FILE *statslogfile = NULL; VOID CloseDebugLog() { if (logfile[DEBUGLOG]) fclose(logfile[DEBUGLOG]); logfile[DEBUGLOG] = NULL; } VOID CloseStatsLog() { if (statslogfile) fclose(statslogfile); statslogfile = NULL; } VOID Debugprintf(const char * format, ...) { char Mess[10000]; va_list(arglist); va_start(arglist, format); vsnprintf(Mess, sizeof(Mess), format, arglist); strcat(Mess, "\r\n"); printf("%s", Mess); WriteLog(Mess, DEBUGLOG); return; } VOID WriteDebugLog(int Level, const char * format, ...) { char Mess[10000]; va_list(arglist); va_start(arglist, format); vsnprintf(Mess, sizeof(Mess), format, arglist); strcat(Mess, "\n"); if (Level <= ConsoleLogLevel) printf("%s", Mess); if (!DebugLog) return; WriteLog(Mess, DEBUGLOG); return; } VOID WriteExceptionLog(const char * format, ...) { char Mess[10000]; va_list(arglist); va_start(arglist, format); vsnprintf(Mess, sizeof(Mess), format, arglist); strcat(Mess, "\n"); printf("%s", Mess); WriteLog(Mess, EXCEPTLOG); fclose(logfile[EXCEPTLOG]); logfile[EXCEPTLOG] = NULL; return; } VOID Statsprintf(const char * format, ...) { char Mess[10000]; va_list(arglist); UCHAR Value[100]; char timebuf[32]; struct timespec tp; int hh; int mm; int ss; clock_gettime(CLOCK_REALTIME, &tp); va_start(arglist, format); vsnprintf(Mess, sizeof(Mess), format, arglist); strcat(Mess, "\n"); ss = tp.tv_sec % 86400; // Secs int day hh = ss / 3600; mm = (ss - (hh * 3600)) / 60; ss = ss % 60; sprintf(timebuf, "%02d:%02d:%02d.%03d ", hh, mm, ss, (int)tp.tv_nsec/1000000); if (statslogfile == NULL) { struct tm * tm; time_t T; T = time(NULL); tm = gmtime(&T); sprintf(Value, "%s%d_%04d%02d%02d.log", LogName[2], port, tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); if ((statslogfile = fopen(Value, "ab")) == NULL) { perror(Value); return; } else { fputs(timebuf, statslogfile); fputs("\n", statslogfile); } } fputs(Mess, statslogfile); printf("%s", Mess); return; } void printtick(char * msg) { Debugprintf("%s %i", msg, Now - LastNow); LastNow = Now; } struct timespec time_start; unsigned int getTicks() { struct timespec tp; clock_gettime(CLOCK_MONOTONIC, &tp); return (tp.tv_sec - time_start.tv_sec) * 1000 + (tp.tv_nsec - time_start.tv_nsec) / 1000000; } void PlatformSleep(int mS) { if (SerialMode) SerialHostPoll(); else TCPHostPoll(); Sleep(mS); if (PKTLEDTimer && Now > PKTLEDTimer) { PKTLEDTimer = 0; SetLED(PKTLED, 0); // turn off packet rxed led } } // PTT via GPIO code #ifdef __ARM_ARCH #define PI_INPUT 0 #define PI_OUTPUT 1 #define PI_ALT0 4 #define PI_ALT1 5 #define PI_ALT2 6 #define PI_ALT3 7 #define PI_ALT4 3 #define PI_ALT5 2 // Set GPIO pin as output and set low extern int pttGPIOPin; extern BOOL pttGPIOInvert; void SetupGPIOPTT() { if (pttGPIOPin == -1) { WriteDebugLog(LOGALERT, "GPIO PTT disabled"); RadioControl = FALSE; useGPIO = FALSE; } else { if (pttGPIOPin < 0) { pttGPIOInvert = TRUE; pttGPIOPin = -pttGPIOPin; } gpioSetMode(pttGPIOPin, PI_OUTPUT); gpioWrite(pttGPIOPin, pttGPIOInvert ? 1 : 0); WriteDebugLog(LOGALERT, "Using GPIO pin %d for PTT", pttGPIOPin); RadioControl = TRUE; useGPIO = TRUE; } } #endif static void sigterm_handler(int sig) { printf("terminating on SIGTERM\n"); blnClosing = TRUE; } static void sigint_handler(int sig) { printf("terminating on SIGINT\n"); blnClosing = TRUE; } char * PortString = NULL; void main(int argc, char * argv[]) { struct timespec tp; struct sigaction act; // Sleep(1000); // Give LinBPQ time to complete init if exec'ed by linbpq processargs(argc, argv); if (LogDir[0]) { sprintf(&LogName[0][0], "%s/%s", LogDir, "ARDOPDebug"); sprintf(&LogName[1][0], "%s/%s", LogDir, "ARDOPException"); sprintf(&LogName[2][0], "%s/%s", LogDir, "ARDOPSession"); } setlinebuf(stdout); // So we can redirect output to file and tail Debugprintf("%s Version %s", ProductName, ProductVersion); if (HostPort[0]) { char *pkt = strlop(HostPort, '/'); if (_memicmp(HostPort, "COM", 3) == 0) { SerialMode = 1; } else port = atoi(HostPort); if (pkt) pktport = atoi(pkt); } if (CATPort[0]) { char * Baud = strlop(CATPort, ':'); if (Baud) CATBAUD = atoi(Baud); hCATDevice = OpenCOMPort(CATPort, CATBAUD, FALSE, FALSE, FALSE, 0); } if (PTTPort[0]) { int fd; if (strstr(PTTPort, "hidraw")) { // Linux - Param is HID Device, eg /dev/hidraw0 CM108Device = strdup(PTTPort); fd = open (CM108Device, O_WRONLY); if (fd == -1) { Debugprintf ("Could not open %s for write, errno=%d", CM108Device, errno); } else { close (fd); PTTMode = PTTCM108; RadioControl = TRUE; Debugprintf ("Using %s for PTT", CM108Device); } } else { char * Baud = strlop(PTTPort, ':'); char * pin = strlop(PTTPort, '='); if (Baud) PTTBAUD = atoi(Baud); if (strcmp(CATPort, PTTPort) == 0) { hPTTDevice = hCATDevice; } else { if (stricmp(PTTPort, "GPIO") == 0) { // Initialise GPIO for PTT if available #ifdef __ARM_ARCH if (gpioInitialise() == 0) { printf("GPIO interface for PTT available\n"); gotGPIO = TRUE; if (pin) pttGPIOPin = atoi(pin); else pttGPIOPin = 17; SetupGPIOPTT(); } else printf("Couldn't initialise GPIO interface for PTT\n"); #else printf("GPIO interface for PTT not available on this platform\n"); #endif } else // Not GPIO { if (Baud) { // Could be IPADDR:PORT or COMPORT:SPEED. See if first part is valid ip address struct sockaddr_in * destaddr = (struct sockaddr_in *)&HamlibAddr; destaddr->sin_family = AF_INET; destaddr->sin_addr.s_addr = inet_addr(PTTPort); destaddr->sin_port = htons(atoi(Baud)); if (destaddr->sin_addr.s_addr != INADDR_NONE) { useHamLib = 1; WriteDebugLog(LOGALERT, "Using Hamlib at %s:%s for PTT", PTTPort, Baud); RadioControl = TRUE; PTTMode = PTTHAMLIB; } else PTTBAUD = atoi(Baud); } if (useHamLib == 0) hPTTDevice = OpenCOMPort(PTTPort, PTTBAUD, FALSE, FALSE, FALSE, 0); } } } } if (hCATDevice) { WriteDebugLog(LOGALERT, "CAT Control on port %s", CATPort); COMSetRTS(hPTTDevice); COMSetDTR(hPTTDevice); if (PTTOffCmdLen) { WriteDebugLog(LOGALERT, "PTT using CAT Port", CATPort); RadioControl = TRUE; } } else { // Warn of -u and -k defined but no CAT Port if (PTTOffCmdLen) { WriteDebugLog(LOGALERT, "Warning PTT Off string defined but no CAT port", CATPort); } } if (hPTTDevice) { WriteDebugLog(LOGALERT, "Using RTS on port %s for PTT", PTTPort); COMClearRTS(hPTTDevice); COMClearDTR(hPTTDevice); RadioControl = TRUE; } initdisplay(); if (SerialMode) Debugprintf("ARDOPC Using a pseudotty symlinked to %s", HostPort); else Debugprintf("ARDOPC listening on port %d", port); if (UseKISS && pktport) Debugprintf("ARDOPC listening for KISS frames on port %d", pktport); // Get Time Reference clock_gettime(CLOCK_MONOTONIC, &time_start); LastNow = getTicks(); // Trap signals memset (&act, '\0', sizeof(act)); act.sa_handler = &sigint_handler; if (sigaction(SIGINT, &act, NULL) < 0) perror ("SIGINT"); act.sa_handler = &sigterm_handler; if (sigaction(SIGTERM, &act, NULL) < 0) perror ("SIGTERM"); act.sa_handler = SIG_IGN; if (sigaction(SIGHUP, &act, NULL) < 0) perror ("SIGHUP"); if (sigaction(SIGPIPE, &act, NULL) < 0) perror ("SIGPIPE"); ardopmain(); // if PTY used, remove it if (SerialMode) unlink (HostPort); } void txSleep(int mS) { // called while waiting for next TX buffer or to delay response. // Run background processes if (SerialMode) SerialHostPoll(); else TCPHostPoll(); Sleep(mS); if (PKTLEDTimer && Now > PKTLEDTimer) { PKTLEDTimer = 0; SetLED(PKTLED, 0); // turn off packet rxed led } } // ALSA Code #define true 1 #define false 0 snd_pcm_t * playhandle = NULL; snd_pcm_t * rechandle = NULL; int m_playchannels = 1; int m_recchannels = 1; char SavedCaptureDevice[256]; // Saved so we can reopen char SavedPlaybackDevice[256]; int Savedplaychannels = 1; int SavedCaptureRate; int SavedPlaybackRate; // This rather convoluted process simplifies marshalling from Managed Code char ** WriteDevices = NULL; int WriteDeviceCount = 0; char ** ReadDevices = NULL; int ReadDeviceCount = 0; // Routine to check that library is available int CheckifLoaded() { // Prevent CTRL/C from closing the TNC // (This causes problems if the TNC is started by LinBPQ) signal(SIGHUP, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGPIPE, SIG_IGN); return TRUE; } int GetOutputDeviceCollection() { // Get all the suitable devices and put in a list for GetNext to return snd_ctl_t *handle= NULL; snd_pcm_t *pcm= NULL; snd_ctl_card_info_t *info; snd_pcm_info_t *pcminfo; snd_pcm_hw_params_t *pars; snd_pcm_format_mask_t *fmask; char NameString[256]; Debugprintf("Playback Devices\n"); CloseSoundCard(); // free old struct if called again // while (WriteDeviceCount) // { // WriteDeviceCount--; // free(WriteDevices[WriteDeviceCount]); // } // if (WriteDevices) // free(WriteDevices); WriteDevices = NULL; WriteDeviceCount = 0; // Add virtual device ARDOP so ALSA plugins can be used if needed WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); WriteDevices[WriteDeviceCount++] = strdup("ARDOP"); // Get Device List from ALSA snd_ctl_card_info_alloca(&info); snd_pcm_info_alloca(&pcminfo); snd_pcm_hw_params_alloca(&pars); snd_pcm_format_mask_alloca(&fmask); char hwdev[80]; unsigned min, max, ratemin, ratemax; int card, err, dev, nsubd; snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; card = -1; if (snd_card_next(&card) < 0) { Debugprintf("No Devices"); return 0; } if (playhandle) snd_pcm_close(playhandle); playhandle = NULL; while (card >= 0) { sprintf(hwdev, "hw:%d", card); err = snd_ctl_open(&handle, hwdev, 0); err = snd_ctl_card_info(handle, info); Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info)); dev = -1; if(snd_ctl_pcm_next_device(handle, &dev) < 0) { // Card has no devices snd_ctl_close(handle); goto nextcard; } while (dev >= 0) { snd_pcm_info_set_device(pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); err = snd_ctl_pcm_info(handle, pcminfo); if (err == -ENOENT) goto nextdevice; nsubd = snd_pcm_info_get_subdevices_count(pcminfo); Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); sprintf(hwdev, "hw:%d,%d", card, dev); err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); if (err) { Debugprintf("Error %d opening output device", err); goto nextdevice; } // Get parameters for this device err = snd_pcm_hw_params_any(pcm, pars); snd_pcm_hw_params_get_channels_min(pars, &min); snd_pcm_hw_params_get_channels_max(pars, &max); snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); if( min == max ) if( min == 1 ) Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); else Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); else Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); // Add device to list sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); WriteDevices = realloc(WriteDevices,(WriteDeviceCount + 1) * sizeof(WriteDevices)); WriteDevices[WriteDeviceCount++] = strdup(NameString); snd_pcm_close(pcm); pcm= NULL; nextdevice: if (snd_ctl_pcm_next_device(handle, &dev) < 0) break; } snd_ctl_close(handle); nextcard: Debugprintf(""); if (snd_card_next(&card) < 0) // No more cards break; } return WriteDeviceCount; } int GetNextOutputDevice(char * dest, int max, int n) { if (n >= WriteDeviceCount) return 0; strcpy(dest, WriteDevices[n]); return strlen(dest); } int GetInputDeviceCollection() { // Get all the suitable devices and put in a list for GetNext to return snd_ctl_t *handle= NULL; snd_pcm_t *pcm= NULL; snd_ctl_card_info_t *info; snd_pcm_info_t *pcminfo; snd_pcm_hw_params_t *pars; snd_pcm_format_mask_t *fmask; char NameString[256]; Debugprintf("Capture Devices\n"); ReadDevices = NULL; ReadDeviceCount = 0; // Add virtual device ARDOP so ALSA plugins can be used if needed ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); ReadDevices[ReadDeviceCount++] = strdup("ARDOP"); // Get Device List from ALSA snd_ctl_card_info_alloca(&info); snd_pcm_info_alloca(&pcminfo); snd_pcm_hw_params_alloca(&pars); snd_pcm_format_mask_alloca(&fmask); char hwdev[80]; unsigned min, max, ratemin, ratemax; int card, err, dev, nsubd; snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; card = -1; if(snd_card_next(&card) < 0) { Debugprintf("No Devices"); return 0; } if (rechandle) snd_pcm_close(rechandle); rechandle = NULL; while(card >= 0) { sprintf(hwdev, "hw:%d", card); err = snd_ctl_open(&handle, hwdev, 0); err = snd_ctl_card_info(handle, info); Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info)); dev = -1; if (snd_ctl_pcm_next_device(handle, &dev) < 0) // No Devicdes { snd_ctl_close(handle); goto nextcard; } while(dev >= 0) { snd_pcm_info_set_device(pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); err= snd_ctl_pcm_info(handle, pcminfo); if (err == -ENOENT) goto nextdevice; nsubd= snd_pcm_info_get_subdevices_count(pcminfo); Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); sprintf(hwdev, "hw:%d,%d", card, dev); err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); if (err) { Debugprintf("Error %d opening input device", err); goto nextdevice; } err = snd_pcm_hw_params_any(pcm, pars); snd_pcm_hw_params_get_channels_min(pars, &min); snd_pcm_hw_params_get_channels_max(pars, &max); snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); if( min == max ) if( min == 1 ) Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); else Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); else Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); // Debugprintf("%s", NameString); ReadDevices = realloc(ReadDevices,(ReadDeviceCount + 1) * sizeof(ReadDevices)); ReadDevices[ReadDeviceCount++] = strdup(NameString); snd_pcm_close(pcm); pcm= NULL; nextdevice: if (snd_ctl_pcm_next_device(handle, &dev) < 0) break; } snd_ctl_close(handle); nextcard: Debugprintf(""); if (snd_card_next(&card) < 0 ) break; } return ReadDeviceCount; } int GetNextInputDevice(char * dest, int max, int n) { if (n >= ReadDeviceCount) return 0; strcpy(dest, ReadDevices[n]); return strlen(dest); } int OpenSoundPlayback(char * PlaybackDevice, int m_sampleRate, int channels, char * ErrorMsg) { int err = 0; char buf1[100]; char * ptr; if (playhandle) { snd_pcm_close(playhandle); playhandle = NULL; } strcpy(SavedPlaybackDevice, PlaybackDevice); // Saved so we can reopen in error recovery SavedPlaybackRate = m_sampleRate; strcpy(buf1, PlaybackDevice); ptr = strchr(buf1, ' '); if (ptr) *ptr = 0; // Get Device part of name snd_pcm_hw_params_t *hw_params; if ((err = snd_pcm_open(&playhandle, buf1, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { if (ErrorMsg) sprintf (ErrorMsg, "cannot open playback audio device %s (%s)", buf1, snd_strerror(err)); else Debugprintf("cannot open playback audio device %s (%s)", buf1, snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { Debugprintf("cannot allocate hardware parameter structure (%s)", snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_any (playhandle, hw_params)) < 0) { Debugprintf("cannot initialize hardware parameter structure (%s)", snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_set_access (playhandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { Debugprintf("cannot set playback access type (%s)", snd_strerror (err)); return false; } if ((err = snd_pcm_hw_params_set_format (playhandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { Debugprintf("cannot setplayback sample format (%s)", snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_set_rate (playhandle, hw_params, m_sampleRate, 0)) < 0) { if (ErrorMsg) sprintf (ErrorMsg, "cannot set playback sample rate (%s)", snd_strerror(err)); else Debugprintf("cannot set playback sample rate (%s)", snd_strerror(err)); return false; } // Initial call has channels set to 1. Subequent ones set to what worked last time if ((err = snd_pcm_hw_params_set_channels (playhandle, hw_params, channels)) < 0) { Debugprintf("cannot set play channel count to %d (%s)", channels, snd_strerror(err)); if (channels == 2) return false; // Shouldn't happen as should have worked before channels = 2; if ((err = snd_pcm_hw_params_set_channels (playhandle, hw_params, 2)) < 0) { Debugprintf("cannot play set channel count to 2 (%s)", snd_strerror(err)); return false; } } // Debugprintf("Play using %d channels", channels); if ((err = snd_pcm_hw_params (playhandle, hw_params)) < 0) { Debugprintf("cannot set parameters (%s)", snd_strerror(err)); return false; } snd_pcm_hw_params_free(hw_params); if ((err = snd_pcm_prepare (playhandle)) < 0) { Debugprintf("cannot prepare audio interface for use (%s)", snd_strerror(err)); return false; } Savedplaychannels = m_playchannels = channels; MaxAvail = snd_pcm_avail_update(playhandle); // Debugprintf("Playback Buffer Size %d", (int)MaxAvail); return true; } int OpenSoundCapture(char * CaptureDevice, int m_sampleRate, char * ErrorMsg) { int err = 0; char buf1[100]; char * ptr; snd_pcm_hw_params_t *hw_params; if (rechandle) { snd_pcm_close(rechandle); rechandle = NULL; } strcpy(SavedCaptureDevice, CaptureDevice); // Saved so we can reopen in error recovery SavedCaptureRate = m_sampleRate; strcpy(buf1, CaptureDevice); ptr = strchr(buf1, ' '); if (ptr) *ptr = 0; // Get Device part of name if ((err = snd_pcm_open (&rechandle, buf1, SND_PCM_STREAM_CAPTURE, 0)) < 0) { if (ErrorMsg) sprintf (ErrorMsg, "cannot open capture audio device %s (%s)", buf1, snd_strerror(err)); else Debugprintf("cannot open capture audio device %s (%s)", buf1, snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { Debugprintf("cannot allocate capture hardware parameter structure (%s)", snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_any (rechandle, hw_params)) < 0) { Debugprintf("cannot initialize capture hardware parameter structure (%s)", snd_strerror(err)); return false; } //craiger add frames per period fpp = 600; dir = 0; if ((err = snd_pcm_hw_params_set_period_size_near (rechandle, hw_params, &fpp, &dir)) < 0) { Debugprintf("snd_pcm_hw_params_set_period_size_near failed (%s)", snd_strerror (err)); return false; } if ((err = snd_pcm_hw_params_set_access (rechandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { Debugprintf("cannot set capture access type (%s)", snd_strerror (err)); return false; } if ((err = snd_pcm_hw_params_set_format (rechandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { Debugprintf("cannot set capture sample format (%s)", snd_strerror(err)); return false; } if ((err = snd_pcm_hw_params_set_rate (rechandle, hw_params, m_sampleRate, 0)) < 0) { if (ErrorMsg) sprintf (ErrorMsg, "cannot set capture sample rate (%s)", snd_strerror(err)); else Debugprintf("cannot set capture sample rate (%s)", snd_strerror(err)); return false; } m_recchannels = 1; if (UseLeftRX == 0 || UseRightRX == 0) m_recchannels = 2; // L/R implies stereo if ((err = snd_pcm_hw_params_set_channels (rechandle, hw_params, m_recchannels)) < 0) { if (ErrorMsg) sprintf (ErrorMsg, "cannot set rec channel count to %d (%s)" ,m_recchannels, snd_strerror(err)); else Debugprintf("cannot set rec channel count to %d (%s)", m_recchannels, snd_strerror(err)); if (m_recchannels == 1) { m_recchannels = 2; if ((err = snd_pcm_hw_params_set_channels (rechandle, hw_params, 2)) < 0) { Debugprintf("cannot set rec channel count to 2 (%s)", snd_strerror(err)); return false; } if (ErrorMsg) sprintf (ErrorMsg, "Record channel count set to 2 (%s)", snd_strerror(err)); else Debugprintf("Record channel count set to 2 (%s)", snd_strerror(err)); } } if ((err = snd_pcm_hw_params (rechandle, hw_params)) < 0) { fprintf (stderr, "cannot set parameters (%s)", snd_strerror(err)); return false; } snd_pcm_hw_params_free(hw_params); if ((err = snd_pcm_prepare (rechandle)) < 0) { Debugprintf("cannot prepare audio interface for use (%s)", snd_strerror(err)); return FALSE; } // Debugprintf("Capture using %d channels", m_recchannels); int i; short buf[256]; for (i = 0; i < 10; ++i) { if ((err = snd_pcm_readi (rechandle, buf, 128)) != 128) { Debugprintf("read from audio interface failed (%s)", snd_strerror (err)); } } // Debugprintf("Read got %d", err); return TRUE; } int OpenSoundCard(char * CaptureDevice, char * PlaybackDevice, int c_sampleRate, int p_sampleRate, char * ErrorMsg) { int Channels = 1; if (UseLeftRX == 1 && UseRightRX == 1) { printf("Using Both Channels of soundcard for RX\n"); } else { if (UseLeftRX == 0) printf("Using Right Channel of soundcard for RX\n"); if (UseRightRX == 0) printf("Using Left Channel of soundcard for RX\n"); } if (UseLeftTX == 1 && UseRightTX == 1) { printf("Using Both Channels of soundcard for TX\n"); } else { if (UseLeftTX == 0) printf("Using Right Channel of soundcard for TX\n"); if (UseRightTX == 0) printf("Using Left Channel of soundcard for TX\n"); } Debugprintf("Opening Playback Device %s Rate %d", PlaybackDevice, p_sampleRate); if (UseLeftTX == 0 || UseRightTX == 0) Channels = 2; // L or R implies stereo if (OpenSoundPlayback(PlaybackDevice, p_sampleRate, Channels, ErrorMsg)) { #ifdef SHARECAPTURE // Close playback device so it can be shared if (playhandle) { snd_pcm_close(playhandle); playhandle = NULL; } #endif Debugprintf("Opening Capture Device %s Rate %d", CaptureDevice, c_sampleRate); return OpenSoundCapture(CaptureDevice, c_sampleRate, ErrorMsg); } else return false; } int CloseSoundCard() { if (rechandle) { snd_pcm_close(rechandle); rechandle = NULL; } if (playhandle) { snd_pcm_close(playhandle); playhandle = NULL; } return 0; } int SoundCardWrite(short * input, unsigned int nSamples) { unsigned int i = 0, n; int ret, err, res; snd_pcm_sframes_t avail, maxavail; snd_pcm_status_t *status = NULL; if (playhandle == NULL) return 0; // Stop Capture if (rechandle) { snd_pcm_close(rechandle); rechandle = NULL; } avail = snd_pcm_avail_update(playhandle); // Debugprintf("avail before play returned %d", (int)avail); if (avail < 0) { if (avail != -32) Debugprintf("Playback Avail Recovering from %d ..", (int)avail); snd_pcm_recover(playhandle, avail, 1); avail = snd_pcm_avail_update(playhandle); if (avail < 0) Debugprintf("avail play after recovery returned %d", (int)avail); } maxavail = avail; // Debugprintf("Samples %d Tosend %d Avail %d", SampleNo, nSamples, (int)avail); while (avail < nSamples) { txSleep(100); avail = snd_pcm_avail_update(playhandle); // Debugprintf("After Sleep Tosend %d Avail %d", nSamples, (int)avail); } ret = PackSamplesAndSend(input, nSamples); return ret; } int PackSamplesAndSend(short * input, int nSamples) { unsigned short samples[256000]; unsigned short * sampptr = samples; unsigned int n; int ret; snd_pcm_sframes_t avail; // Convert byte stream to int16 (watch endianness) if (m_playchannels == 1) { for (n = 0; n < nSamples; n++) { *(sampptr++) = input[0]; input ++; } } else { int i = 0; for (n = 0; n < nSamples; n++) { if (UseLeftRX) *(sampptr++) = input[0]; else *(sampptr++) = 0; if (UseRightTX) *(sampptr++) = input[0]; else *(sampptr++) = 0; input ++; } } ret = snd_pcm_writei(playhandle, samples, nSamples); if (ret < 0) { // Debugprintf("Write Recovering from %d ..", ret); snd_pcm_recover(playhandle, ret, 1); ret = snd_pcm_writei(playhandle, samples, nSamples); // Debugprintf("Write after recovery returned %d", ret); } avail = snd_pcm_avail_update(playhandle); return ret; } /* int xSoundCardClearInput() { short samples[65536]; int n; int ret; int avail; if (rechandle == NULL) return 0; // Clear queue avail = snd_pcm_avail_update(rechandle); if (avail < 0) { Debugprintf("Discard Recovering from %d ..", avail); if (rechandle) { snd_pcm_close(rechandle); rechandle = NULL; } OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, NULL); avail = snd_pcm_avail_update(rechandle); } while (avail) { if (avail > 65536) avail = 65536; ret = snd_pcm_readi(rechandle, samples, avail); // Debugprintf("Discarded %d samples from card", ret); avail = snd_pcm_avail_update(rechandle); // Debugprintf("Discarding %d samples from card", avail); } return 0; } */ int SoundCardRead(short * input, unsigned int nSamples) { short samples[65536]; int n; int ret; int avail; int start; if (rechandle == NULL) return 0; avail = snd_pcm_avail_update(rechandle); if (avail < 0) { Debugprintf("avail Recovering from %d ..", avail); if (rechandle) { snd_pcm_close(rechandle); rechandle = NULL; } OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, NULL); // snd_pcm_recover(rechandle, avail, 0); avail = snd_pcm_avail_update(rechandle); Debugprintf("After avail recovery %d ..", avail); } if (avail < nSamples) return 0; // Debugprintf("ALSARead available %d", avail); ret = snd_pcm_readi(rechandle, samples, nSamples); if (ret < 0) { Debugprintf("RX Error %d", ret); // snd_pcm_recover(rechandle, avail, 0); if (rechandle) { snd_pcm_close(rechandle); rechandle = NULL; } OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, NULL); // snd_pcm_recover(rechandle, avail, 0); avail = snd_pcm_avail_update(rechandle); Debugprintf("After Read recovery Avail %d ..", avail); return 0; } if (m_recchannels == 1) { for (n = 0; n < ret; n++) { *(input++) = samples[n]; } } else { if (UseLeftRX) start = 0; else start = 1; for (n = start; n < (ret * 2); n+=2) // return alternate { *(input++) = samples[n]; } } return ret; } int PriorSize = 0; int Index = 0; // DMA Buffer being used 0 or 1 int inIndex = 0; // DMA Buffer being used 0 or 1 BOOL DMARunning = FALSE; // Used to start DMA on first write short * SendtoCard(short * buf, int n) { if (Loopback) { // Loop back to decode for testing ProcessNewSamples(buf, 1200); // signed } if (playhandle) SoundCardWrite(&buffer[Index][0], n); // txSleep(10); // Run buckground while waiting return &buffer[Index][0]; } short loopbuff[1200]; // Temp for testing - loop sent samples to decoder // // This generates a nice musical pattern for sound interface testing // for (t = 0; t < sizeof(buffer); ++t) // buffer[t] =((((t * (t >> 8 | t >> 9) & 46 & t >> 8)) ^ (t & t >> 13 | t >> 6)) & 0xFF); void InitSound(BOOL Quiet) { GetInputDeviceCollection(); GetOutputDeviceCollection(); OpenSoundCard(CaptureDevice, PlaybackDevice, 12000, 12000, NULL); } int min = 0, max = 0, lastlevelreport = 0, lastlevelGUI = 0; UCHAR CurrentLevel = 0; // Peak from current samples void PollReceivedSamples() { // Process any captured samples // Ideally call at least every 100 mS, more than 200 will loose data if (SoundCardRead(&inbuffer[0][0], ReceiveSize)) { // returns ReceiveSize or none short * ptr = &inbuffer[0][0]; int i; for (i = 0; i < ReceiveSize; i++) { if (*(ptr) < min) min = *ptr; else if (*(ptr) > max) max = *ptr; ptr++; } displayLevel(max); CurrentLevel = ((max - min) * 75) /32768; // Scale to 150 max if ((Now - lastlevelGUI) > 2000) // 2 Secs { if (WaterfallActive == 0 && SpectrumActive == 0) // Don't need to send as included in Waterfall Line SendtoGUI('L', &CurrentLevel, 1); // Signal Level lastlevelGUI = Now; if ((Now - lastlevelreport) > 10000) // 10 Secs { char HostCmd[64]; lastlevelreport = Now; sprintf(HostCmd, "INPUTPEAKS %d %d", min, max); SendCommandToHostQuiet(HostCmd); WriteDebugLog(LOGDEBUG, "Input peaks = %d, %d", min, max); } min = max = 0; // Every 2 secs } if (Capturing && Loopback == FALSE) ProcessNewSamples(&inbuffer[0][0], ReceiveSize); } } void StopCapture() { Capturing = FALSE; #ifdef SHARECAPTURE // Stopcapture is only called when we are about to transmit, so use it to open plaback device. We don't keep // it open all the time to facilitate sharing. OpenSoundPlayback(SavedPlaybackDevice, SavedPlaybackRate, Savedplaychannels, NULL); #endif } void StartCodec(char * strFault) { strFault[0] = 0; OpenSoundCard(CaptureDevice, PlaybackDevice, 12000, 12000, strFault); } void StopCodec(char * strFault) { strFault[0] = 0; CloseSoundCard(); } void StartCapture() { Capturing = TRUE; DiscardOldSamples(); ClearAllMixedSamples(); State = SearchingForLeader; // Debugprintf("Start Capture"); } void CloseSound() { CloseSoundCard(); } int WriteLog(char * msg, int Log) { FILE *file; char timebuf[128]; struct timespec tp; UCHAR Value[256]; int hh; int mm; int ss; clock_gettime(CLOCK_REALTIME, &tp); if (logfile[Log] == NULL) { struct tm * tm; time_t T; T = time(NULL); tm = gmtime(&T); if (HostPort[0]) sprintf(Value, "%s%s_%04d%02d%02d.log", LogName[Log], HostPort, tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); else sprintf(Value, "%s%d_%04d%02d%02d.log", LogName[Log], port, tm->tm_year +1900, tm->tm_mon+1, tm->tm_mday); if ((logfile[Log] = fopen(Value, "a")) == NULL) return FALSE; } ss = tp.tv_sec % 86400; // Secs int day hh = ss / 3600; mm = (ss - (hh * 3600)) / 60; ss = ss % 60; sprintf(timebuf, "%02d:%02d:%02d.%03d ", hh, mm, ss, (int)tp.tv_nsec/1000000); fputs(timebuf, logfile[Log]); fputs(msg, logfile[Log]); fflush(logfile[Log]); return 0; } VOID WriteSamples(short * buffer, int len) { #ifdef WIN32 fwrite(buffer, 1, len * 2, wavfp1); #endif } unsigned short * SoundInit() { Index = 0; return &buffer[0][0]; } // Called at end of transmission void SoundFlush() { // Append Trailer then send remaining samples snd_pcm_status_t *status = NULL; int err, res; char strFault[100] = ""; int count = 100; int lastavail = 0; int txlenMs = 0; snd_pcm_sframes_t avail; AddTrailer(); // add the trailer. if (Loopback) ProcessNewSamples(&buffer[Index][0], Number); SendtoCard(&buffer[Index][0], Number); usleep(200000); // Wait for tx to complete // samples sent is is in SampleNo, time started in mS is in pttOnTime. // calculate time to stop //craiger // txlenMs = SampleNo / 12 + 200; // 12000 samples per sec. 20 mS TXTAIL txlenMs = SampleNo / 12; // 12000 samples per sec. Debugprintf("Tx Time %d Time till end = %d", txlenMs, (pttOnTime + txlenMs) - Now); while (Now < (pttOnTime + txlenMs)) { //craiger // usleep(2000); usleep(200); } /* while (count-- && playhandle) { snd_pcm_sframes_t avail = snd_pcm_avail_update(playhandle); Debugprintf("Waiting for complete. Avail %d Max %d last %d", avail, MaxAvail, lastavail); snd_pcm_status_alloca(&status); // alloca allocates once per function, does not need a free if ((err=snd_pcm_status(playhandle, status))!=0) { Debugprintf("snd_pcm_status() failed: %s",snd_strerror(err)); break; } res = snd_pcm_status_get_state(status); Debugprintf("PCM Status = %d", res); // Some cards seem to stop sending but not report not running, so also check that avail is decreasing if (res != SND_PCM_STATE_RUNNING || lastavail == avail) // If sound system is not running then it needs data // if (MaxAvail - avail < 100) { // Send complete - Restart Capture break; } lastavail = avail; usleep(50000); } */ OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, strFault); // I think we should turn round the link here. I dont see the point in // waiting for MainPoll #ifdef SHARECAPTURE if (playhandle) { snd_pcm_close(playhandle); playhandle = NULL; } #endif SoundIsPlaying = FALSE; if (blnEnbARQRpt > 0 || blnDISCRepeating) // Start Repeat Timer if frame should be repeated dttNextPlay = Now + intFrameRepeatInterval + extraDelay; KeyPTT(FALSE); // Unkey the Transmitter StartCapture(); return; } VOID RadioPTT(int PTTState) { #ifdef __ARM_ARCH if (useGPIO) gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1-PTTState) : (PTTState))); else #endif { if (PTTMode & PTTRTS) if (PTTState) COMSetRTS(hPTTDevice); else COMClearRTS(hPTTDevice); if (PTTMode & PTTDTR) if (PTTState) COMSetDTR(hPTTDevice); else COMClearDTR(hPTTDevice); if (PTTMode & PTTCI_V) if (PTTState) WriteCOMBlock(hCATDevice, PTTOnCmd, PTTOnCmdLen); else WriteCOMBlock(hCATDevice, PTTOffCmd, PTTOffCmdLen); if (PTTMode & PTTCM108) CM108_set_ptt(PTTState); } } // Function to send PTT TRUE or PTT FALSE commanad to Host or if local Radio control Keys radio PTT const char BoolString[2][6] = {"FALSE", "TRUE"}; BOOL KeyPTT(BOOL blnPTT) { // Returns TRUE if successful False otherwise if (blnLastPTT && !blnPTT) dttStartRTMeasure = Now; // start a measurement on release of PTT. if (!RadioControl) if (blnPTT) SendCommandToHostQuiet("PTT TRUE"); else SendCommandToHostQuiet("PTT FALSE"); else RadioPTT(blnPTT); WriteDebugLog(LOGDEBUG, "[Main.KeyPTT] PTT-%s", BoolString[blnPTT]); blnLastPTT = blnPTT; SetLED(0, blnPTT); return TRUE; } static struct speed_struct { int user_speed; speed_t termios_speed; } speed_table[] = { {300, B300}, {600, B600}, {1200, B1200}, {2400, B2400}, {4800, B4800}, {9600, B9600}, {19200, B19200}, {38400, B38400}, {57600, B57600}, {115200, B115200}, {-1, B0} }; // GPIO access stuff for PTT on PI #ifdef __ARM_ARCH /* tiny_gpio.c 2016-04-30 Public Domain */ #include #include #include #include #include #include #include #include #define GPSET0 7 #define GPSET1 8 #define GPCLR0 10 #define GPCLR1 11 #define GPLEV0 13 #define GPLEV1 14 #define GPPUD 37 #define GPPUDCLK0 38 #define GPPUDCLK1 39 unsigned piModel; unsigned piRev; static volatile uint32_t *gpioReg = MAP_FAILED; #define PI_BANK (gpio>>5) #define PI_BIT (1<<(gpio&0x1F)) /* gpio modes. */ void gpioSetMode(unsigned gpio, unsigned mode) { int reg, shift; reg = gpio/10; shift = (gpio%10) * 3; gpioReg[reg] = (gpioReg[reg] & ~(7<> shift) & 7; } /* Values for pull-ups/downs off, pull-down and pull-up. */ #define PI_PUD_OFF 0 #define PI_PUD_DOWN 1 #define PI_PUD_UP 2 void gpioSetPullUpDown(unsigned gpio, unsigned pud) { *(gpioReg + GPPUD) = pud; usleep(20); *(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT; usleep(20); *(gpioReg + GPPUD) = 0; *(gpioReg + GPPUDCLK0 + PI_BANK) = 0; } int gpioRead(unsigned gpio) { if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1; else return 0; } void gpioWrite(unsigned gpio, unsigned level) { if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; } void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level) { if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; usleep(pulseLen); if (level != 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; } /* Bit (1<