qtsoundmodem/ALSASound.c

2144 lines
45 KiB
C
Raw Permalink Normal View History

2023-09-04 19:06:44 +01:00
/*
Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
This file is part of QtSoundModem
QtSoundModem 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.
QtSoundModem 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 QtSoundModem. If not, see http://www.gnu.org/licenses
*/
//#define TXSILENCE
// UZ7HO Soundmodem Port by John Wiseman G8BPQ
//
// Audio interface Routine
// Passes audio samples to/from the sound interface
// 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
#include <alsa/asoundlib.h>
#include <signal.h>
#include <termios.h>
#include <sys/ioctl.h>
#include "UZ7HOStuff.h"
#define VOID void
2024-07-23 21:26:30 +01:00
char * strlop(char * buf, char delim);
int gethints();
struct timespec pttclk;
2023-09-04 19:06:44 +01:00
extern int Closing;
int SoundMode = 0;
int stdinMode = 0;
2024-07-23 21:26:30 +01:00
int onlyMixSnoop = 0;
int txLatency;
2023-09-04 19:06:44 +01:00
//#define SHARECAPTURE // if defined capture device is opened and closed for each transission
#define HANDLE int
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(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits);
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[]);
void PollReceivedSamples();
HANDLE OpenCOMPort(char * Port, 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);
int oss_read(short * samples, int nSamples);
int oss_write(short * ptr, int len);
int oss_flush();
int oss_audio_open(char * adevice_in, char * adevice_out);
void oss_audio_close();
int listpulse();
int pulse_read(short * ptr, int len);
int pulse_write(short * ptr, int len);
int pulse_flush();
int pulse_audio_open(char * adevice_in, char * adevice_out);
void pulse_audio_close();
int initdisplay();
extern BOOL blnDISCRepeating;
extern BOOL UseKISS; // Enable Packet (KISS) interface
extern short * DMABuffer;
2023-09-12 21:38:15 +01:00
#define MaxReceiveSize 2048 // Enough for 9600
#define MaxSendSize 4096
short buffer[2][MaxSendSize * 2]; // Two Transfer/DMA buffers of 0.1 Sec (x2 for Stereo)
short inbuffer[MaxReceiveSize * 2]; // Input Transfer/ buffers of 0.1 Sec (x2 for Stereo)
extern short * DMABuffer;
extern int Number;
int ReceiveSize = 512;
int SendSize = 1024;
int using48000 = 0;
int SampleRate = 12000;
2023-09-04 19:06:44 +01:00
BOOL UseLeft = TRUE;
BOOL UseRight = TRUE;
char LogDir[256] = "";
void WriteDebugLog(char * Msg);
VOID Debugprintf(const char * format, ...)
{
char Mess[10000];
va_list(arglist);
va_start(arglist, format);
vsprintf(Mess, format, arglist);
WriteDebugLog(Mess);
return;
}
void Sleep(int mS)
{
usleep(mS * 1000);
return;
}
// Windows and ALSA work with signed samples +- 32767
// STM32 and Teensy DAC uses unsigned 0 - 4095
BOOL Loopback = FALSE;
//BOOL Loopback = TRUE;
char CaptureDevice[80] = "plughw:0,0";
char PlaybackDevice[80] = "plughw:0,0";
char * CaptureDevices = CaptureDevice;
char * PlaybackDevices = CaptureDevice;
int CaptureIndex = 0;
int PlayBackIndex = 0;
int Ticks;
int LastNow;
extern int Number; // Number waiting to be sent
snd_pcm_sframes_t MaxAvail;
#include <stdarg.h>
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 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)
{
Sleep(mS);
}
// 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
void SetupGPIOPTT()
{
if (pttGPIOPin == -1)
{
Debugprintf("GPIO PTT disabled");
useGPIO = FALSE;
}
else
{
if (pttGPIOPin < 0) {
pttGPIOInvert = TRUE;
pttGPIOPin = -pttGPIOPin;
}
gpioSetMode(pttGPIOPin, PI_OUTPUT);
gpioWrite(pttGPIOPin, pttGPIOInvert ? 1 : 0);
Debugprintf("Using GPIO pin %d for Left/Mono PTT", pttGPIOPin);
if (pttGPIOPinR != -1)
{
gpioSetMode(pttGPIOPinR, PI_OUTPUT);
gpioWrite(pttGPIOPinR, pttGPIOInvert ? 1 : 0);
Debugprintf("Using GPIO pin %d for Right PTT", pttGPIOPin);
}
useGPIO = TRUE;
}
}
#endif
static void sigterm_handler(int n)
{
UNUSED(n);
printf("terminating on SIGTERM\n");
Closing = TRUE;
}
static void sigint_handler(int n)
{
UNUSED(n);
printf("terminating on SIGINT\n");
Closing = TRUE;
}
char * PortString = NULL;
void platformInit()
{
struct sigaction act;
// Sleep(1000); // Give LinBPQ time to complete init if exec'ed by linbpq
// 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");
}
void txSleep(int mS)
{
// called while waiting for next TX buffer. Run background processes
2024-07-23 21:26:30 +01:00
if (mS < 0)
return;
2023-09-04 19:06:44 +01:00
while (mS > 50)
{
PollReceivedSamples(); // discard any received samples
Sleep(50);
mS -= 50;
}
Sleep(mS);
PollReceivedSamples(); // discard any received samples
}
// ALSA Code
#define true 1
#define false 0
snd_pcm_t * playhandle = NULL;
snd_pcm_t * rechandle = NULL;
int m_playchannels = 2;
int m_recchannels = 2;
char SavedCaptureDevice[256]; // Saved so we can reopen
char SavedPlaybackDevice[256];
int Savedplaychannels = 2;
int SavedCaptureRate;
int SavedPlaybackRate;
char CaptureNames[16][256] = { "" };
char PlaybackNames[16][256] = { "" };
int PlaybackCount = 0;
int CaptureCount = 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 (PlaybackCount)
// {
// PlaybackCount--;
// free(PlaybackNames[PlaybackCount]);
// }
// if (PlaybackNames)
// free(PlaybackNames);
PlaybackCount = 0;
// 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));
strcpy(PlaybackNames[PlaybackCount++], 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 PlaybackCount;
}
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");
CaptureCount = 0;
// 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);
strcpy(CaptureNames[CaptureCount++], 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;
}
strcpy(CaptureNames[CaptureCount++], "stdin");
return CaptureCount;
}
int OpenSoundPlayback(char * PlaybackDevice, int m_sampleRate, int channels, int Report)
{
int err = 0;
2024-07-23 21:26:30 +01:00
char buf1[256];
char buf2[256];
2023-09-04 19:06:44 +01:00
char * ptr;
if (playhandle)
{
snd_pcm_close(playhandle);
playhandle = NULL;
}
strcpy(SavedPlaybackDevice, PlaybackDevice); // Saved so we can reopen in error recovery
SavedPlaybackRate = m_sampleRate;
2024-07-23 21:26:30 +01:00
strcpy(buf2, PlaybackDevice);
ptr = strchr(buf2, ' ');
if (ptr) *ptr = 0; // Get Device part of name
if (strstr(buf2, "plug") == 0 && strchr(buf2, ':'))
sprintf(buf1, "plug%s", buf2);
2023-09-04 19:06:44 +01:00
else
2024-07-23 21:26:30 +01:00
strcpy(buf1, buf2);
2023-09-04 19:06:44 +01:00
if (Report)
Debugprintf("Real Device %s", buf1);
snd_pcm_hw_params_t *hw_params;
if ((err = snd_pcm_open(&playhandle, buf1, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0)
{
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) {
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
channels = 2;
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;
}
}
if (Report)
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);
if (Report)
Debugprintf("Playback Buffer Size %d", (int)MaxAvail);
return true;
}
int OpenSoundCapture(char * CaptureDevice, int m_sampleRate, int Report)
{
int err = 0;
2024-07-23 21:26:30 +01:00
char buf1[256];
char buf2[256];
2023-09-04 19:06:44 +01:00
char * ptr;
snd_pcm_hw_params_t *hw_params;
if (strcmp(CaptureDevice, "stdin") == 0)
{
stdinMode = 1;
Debugprintf("Input from stdin");
return TRUE;
}
if (rechandle)
{
snd_pcm_close(rechandle);
rechandle = NULL;
}
strcpy(SavedCaptureDevice, CaptureDevice); // Saved so we can reopen in error recovery
SavedCaptureRate = m_sampleRate;
2024-07-23 21:26:30 +01:00
strcpy(buf2, CaptureDevice);
ptr = strchr(buf2, ' ');
if (ptr) *ptr = 0; // Get Device part of name
if (strstr(buf2, "plug") == 0 && strchr(buf2, ':'))
sprintf(buf1, "plug%s", buf2);
2023-09-04 19:06:44 +01:00
else
2024-07-23 21:26:30 +01:00
strcpy(buf1, buf2);
2023-09-04 19:06:44 +01:00
if (Report)
Debugprintf("Real Device %s", buf1);
if ((err = snd_pcm_open (&rechandle, buf1, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
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;
}
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) {
Debugprintf("cannot set capture sample rate (%s)", snd_strerror(err));
return false;
}
m_recchannels = 2;
if ((err = snd_pcm_hw_params_set_channels(rechandle, hw_params, m_recchannels)) < 0)
{
2024-04-13 17:48:58 +01:00
if (Report)
Debugprintf("cannot set rec channel count to 2 (%s)", snd_strerror(err));
2023-09-04 19:06:44 +01:00
m_recchannels = 1;
if ((err = snd_pcm_hw_params_set_channels(rechandle, hw_params, 1)) < 0)
{
Debugprintf("cannot set rec channel count to 1 (%s)", snd_strerror(err));
return false;
}
if (Report)
Debugprintf("Record channel count set to 1");
}
else
if (Report)
Debugprintf("Record channel count set to 2");
/*
{
unsigned int val = 0;
unsigned int dir = 0, frames = 0;
snd_pcm_hw_params_get_channels(rechandle, &val);
printf("channels = %d\n", val);
snd_pcm_hw_params_get_rate(rechandle, &val, &dir);
printf("rate = %d bps\n", val);
snd_pcm_hw_params_get_period_time(rechandle, &val, &dir);
printf("period time = %d us\n", val);
snd_pcm_hw_params_get_period_size(rechandle, &frames, &dir);
printf("period size = %d frames\n", (int)frames);
snd_pcm_hw_params_get_buffer_time(rechandle, &val, &dir);
printf("buffer time = %d us\n", val);
snd_pcm_hw_params_get_buffer_size(rechandle, (snd_pcm_uframes_t *)&val);
printf("buffer size = %d frames\n", val);
snd_pcm_hw_params_get_periods(rechandle, &val, &dir);
printf("periods per buffer = %d frames\n", val);
}
*/
if ((err = snd_pcm_hw_params (rechandle, hw_params)) < 0)
{
// Try setting some more params Have to reinit params
snd_pcm_hw_params_any(rechandle, hw_params);
snd_pcm_hw_params_set_access(rechandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(rechandle, hw_params, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate(rechandle, hw_params, m_sampleRate, 0);
snd_pcm_hw_params_set_channels(rechandle, hw_params, m_recchannels);
2024-04-13 17:48:58 +01:00
// err = snd_pcm_hw_params_set_buffer_size(rechandle, hw_params, 65536);
2023-09-04 19:06:44 +01:00
2024-04-13 17:48:58 +01:00
// if (err)
// Debugprintf("cannot set buffer size (%s)", snd_strerror(err));
2023-09-04 19:06:44 +01:00
err = snd_pcm_hw_params_set_period_size(rechandle, hw_params, (snd_pcm_uframes_t) { 1024 }, (int) { 0 });
if (err)
Debugprintf("cannot set period size (%s)", snd_strerror(err));
if ((err = snd_pcm_hw_params(rechandle, 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 (rechandle)) < 0) {
Debugprintf("cannot prepare audio interface for use (%s)", snd_strerror(err));
return FALSE;
}
if (Report)
Debugprintf("Capture using %d channels", m_recchannels);
short buf[256];
2024-07-23 21:26:30 +01:00
if ((err = snd_pcm_readi(rechandle, buf, 12)) != 12)
2023-09-04 19:06:44 +01:00
{
2024-07-23 21:26:30 +01:00
Debugprintf("read from audio interface failed (%s)", snd_strerror(err));
2023-09-04 19:06:44 +01:00
}
2024-07-23 21:26:30 +01:00
2023-09-04 19:06:44 +01:00
// Debugprintf("Read got %d", err);
return TRUE;
}
int OpenSoundCard(char * CaptureDevice, char * PlaybackDevice, int c_sampleRate, int p_sampleRate, int Report)
{
int Channels = 1;
if (Report)
Debugprintf("Opening Playback Device %s Rate %d", PlaybackDevice, p_sampleRate);
// if (UseLeft == 0 || UseRight == 0)
Channels = 2; // L or R implies stereo
if (OpenSoundPlayback(PlaybackDevice, p_sampleRate, Channels, Report))
{
#ifdef SHARECAPTURE
// Close playback device so it can be shared
if (playhandle)
{
snd_pcm_close(playhandle);
playhandle = NULL;
}
#endif
if (Report)
Debugprintf("Opening Capture Device %s Rate %d", CaptureDevice, c_sampleRate);
return OpenSoundCapture(CaptureDevice, c_sampleRate, Report);
}
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, int nSamples)
{
unsigned int ret;
snd_pcm_sframes_t avail; // , maxavail;
if (playhandle == NULL)
return 0;
// Stop Capture
2024-07-23 21:26:30 +01:00
// if (rechandle)
// {
// snd_pcm_close(rechandle);
// rechandle = NULL;
// }
2023-09-04 19:06:44 +01:00
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("Tosend %d Avail %d", nSamples, (int)avail);
2023-09-12 21:38:15 +01:00
while (avail < nSamples || (MaxAvail - avail) > SampleRate) // Limit to 1 sec of audio
2023-09-04 19:06:44 +01:00
{
txSleep(10);
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];
int ret;
ret = snd_pcm_writei(playhandle, input, 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);
}
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, int nSamples)
{
short samples[65536];
int n;
int ret;
int avail;
if (SoundMode == 1) // OSS
{
ret = oss_read(samples, nSamples);
}
else if (SoundMode == 2)// Pulse
{
ret = pulse_read(samples, nSamples);
}
else
{
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, 0);
// 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, 0);
// snd_pcm_recover(rechandle, avail, 0);
avail = snd_pcm_avail_update(rechandle);
Debugprintf("After Read recovery Avail %d ..", avail);
return 0;
}
}
if (ret < nSamples)
return 0;
if (m_recchannels == 1)
{
for (n = 0; n < ret; n++)
{
*(input++) = samples[n];
*(input++) = samples[n]; // Duplicate
}
}
else
{
for (n = 0; n < ret * 2; n++) // return all
{
*(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
void ProcessNewSamples(short * Samples, int nSamples);
short * SendtoCard(short * buf, int n)
{
if (Loopback)
{
// Loop back to decode for testing
ProcessNewSamples(buf, 1200); // signed
}
if (SoundMode == 1) // OSS
oss_write(buf, n);
else if (SoundMode == 2) // Pulse
pulse_write(buf, n);
else
{
if (playhandle)
SoundCardWrite(buf, n);
// txSleep(10); // Run buckground while waiting
}
Index = !Index;
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);
short * SoundInit();
void GetSoundDevices()
{
2024-07-23 21:26:30 +01:00
if (onlyMixSnoop)
2023-09-04 19:06:44 +01:00
{
2024-07-23 21:26:30 +01:00
gethints();
2023-09-04 19:06:44 +01:00
}
2024-07-23 21:26:30 +01:00
else
2023-09-04 19:06:44 +01:00
{
2024-07-23 21:26:30 +01:00
if (SoundMode == 0)
{
GetInputDeviceCollection();
GetOutputDeviceCollection();
}
else if (SoundMode == 1)
{
PlaybackCount = 3;
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
strcpy(&PlaybackNames[0][0], "/dev/dsp0");
strcpy(&PlaybackNames[1][0], "/dev/dsp1");
strcpy(&PlaybackNames[2][0], "/dev/dsp2");
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
CaptureCount = 3;
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
strcpy(&CaptureNames[0][0], "/dev/dsp0");
strcpy(&CaptureNames[1][0], "/dev/dsp1");
strcpy(&CaptureNames[2][0], "/dev/dsp2");
}
else if (SoundMode == 2)
{
// Pulse
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
listpulse();
}
2023-09-04 19:06:44 +01:00
}
}
int InitSound(BOOL Quiet)
{
2023-09-12 21:38:15 +01:00
if (using48000)
{
ReceiveSize = 2048;
SendSize = 4096; // 100 mS for now
SampleRate = 48000;
}
else
{
ReceiveSize = 512;
SendSize = 1024;
SampleRate = 12000;
}
2023-09-04 19:06:44 +01:00
GetSoundDevices();
switch (SoundMode)
{
case 0: // ALSA
2023-09-12 21:38:15 +01:00
if (!OpenSoundCard(CaptureDevice, PlaybackDevice, SampleRate, SampleRate, Quiet))
2023-09-04 19:06:44 +01:00
return FALSE;
2023-09-12 21:38:15 +01:00
2023-09-04 19:06:44 +01:00
break;
case 1: // OSS
if (!oss_audio_open(CaptureDevice, PlaybackDevice))
return FALSE;
break;
case 2: // PulseAudio
if (!pulse_audio_open(CaptureDevice, PlaybackDevice))
return FALSE;
break;
}
printf("InitSound %s %s\n", CaptureDevice, PlaybackDevice);
DMABuffer = SoundInit();
return TRUE;
}
int min = 0, max = 0, lastlevelreport = 0, lastlevelGUI = 0;
UCHAR CurrentLevel = 0; // Peak from current samples
2024-04-13 17:48:58 +01:00
UCHAR CurrentLevelR = 0; // Peak from current samples
2023-09-04 19:06:44 +01:00
void PollReceivedSamples()
{
// Process any captured samples
// Ideally call at least every 100 mS, more than 200 will loose data
int bytes;
#ifdef TXSILENCE
SendSilence(); // send silence (attempt to fix CM delay issue)
#endif
if (stdinMode)
{
// will block if no input. May get less, in which case wait a bit then try to read rest
// rtl_udp outputs mono samples
short input[1200];
short * ptr1, *ptr2;
int n = 20; // Max Wait
bytes = read(STDIN_FILENO, input, ReceiveSize * 2); // 4 = Stereo 2 bytes per sample
while (bytes < ReceiveSize * 2 && n--)
{
Sleep(50); //mS
bytes += read(STDIN_FILENO, &input[bytes / 2], (ReceiveSize * 2) - bytes);
}
// if still not enough, too bad!
if (bytes != ReceiveSize * 2)
2024-07-23 21:26:30 +01:00
{
// This seems to happen occasionally even when we shouldn't be in stdin mode. Exit
2023-09-04 19:06:44 +01:00
Debugprintf("Short Read %d", bytes);
2024-07-23 21:26:30 +01:00
closeTraceLog();
Closing = TRUE;
sleep(1);
exit(1);
}
2023-09-04 19:06:44 +01:00
// convert to stereo
ptr1 = input;
ptr2 = inbuffer;
n = ReceiveSize;
while (n--)
{
*ptr2++ = *ptr1;
*ptr2++ = *ptr1++;
}
}
else
bytes = SoundCardRead(inbuffer, ReceiveSize); // returns ReceiveSize or none
if (bytes > 0)
{
short * ptr = inbuffer;
int i;
for (i = 0; i < ReceiveSize; i++)
{
if (*(ptr) < min)
min = *ptr;
else if (*(ptr) > max)
max = *ptr;
ptr++;
}
CurrentLevel = ((max - min) * 75) /32768; // Scale to 150 max
if ((Now - lastlevelGUI) > 2000) // 2 Secs
{
lastlevelGUI = Now;
2024-07-23 21:26:30 +01:00
if ((Now - lastlevelreport) > 60000) // 60 Secs
2023-09-04 19:06:44 +01:00
{
char HostCmd[64];
lastlevelreport = Now;
sprintf(HostCmd, "INPUTPEAKS %d %d", min, max);
Debugprintf("Input peaks = %d, %d", min, max);
}
min = max = 0; // Every 2 secs
}
ProcessNewSamples(inbuffer, 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 StartCapture()
{
Capturing = TRUE;
// Debugprintf("Start Capture");
}
void CloseSound()
{
switch (SoundMode)
{
case 0: // ALSA
CloseSoundCard();
return;
case 1: // OSS
oss_audio_close();
return;
case 2: // PulseAudio
pulse_audio_close();
return;
}
}
short * SoundInit()
{
Index = 0;
return &buffer[0][0];
}
// Called at end of transmission
2024-07-23 21:26:30 +01:00
int useTimedPTT = 1;
extern int SampleNo;
int pttOnTime();
2023-09-04 19:06:44 +01:00
void SoundFlush()
{
// Append Trailer then send remaining samples
snd_pcm_status_t *status = NULL;
int err, res;
int lastavail = 0;
if (Loopback)
ProcessNewSamples(&buffer[Index][0], Number);
SendtoCard(&buffer[Index][0], Number);
// Wait for tx to complete
2024-07-23 21:26:30 +01:00
// Debugprintf("Flush Soundmode = %d", SoundMode);
2023-09-04 19:06:44 +01:00
if (SoundMode == 0) // ALSA
{
2024-07-23 21:26:30 +01:00
if (useTimedPTT)
2023-09-04 19:06:44 +01:00
{
2024-07-23 21:26:30 +01:00
// Calulate PTT Time from Number of samples and samplerate
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
// samples sent is is in SampleNo, Time PTT was raised in timeval pttclk
// txLatency is extra ptt time to compenstate for time soundcard takes to start outputting samples
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
struct timespec pttnow;
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
clock_gettime(CLOCK_MONOTONIC, &pttnow);
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
time_t pttontimemS = (pttclk.tv_sec * 1000) + (pttclk.tv_nsec / 1000000);
time_t nowtimemS = (pttnow.tv_sec * 1000) + (pttnow.tv_nsec / 1000000);
// We have already added latency to tail, so don't add again
int txlenMs = (1000 * SampleNo / TX_Samplerate); // 12000 samples per sec.
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
Debugprintf("Tx Time %d Time till end = %d", txlenMs, (nowtimemS - pttontimemS));
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
txSleep(txlenMs - (nowtimemS - pttontimemS));
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
}
else
{
usleep(100000);
while (1 && playhandle)
2023-09-04 19:06:44 +01:00
{
2024-07-23 21:26:30 +01:00
snd_pcm_sframes_t avail = snd_pcm_avail_update(playhandle);
2023-09-04 19:06:44 +01:00
2024-07-23 21:26:30 +01:00
// Debugprintf("Waiting for complete. Avail %d Max %d", avail, MaxAvail);
snd_pcm_status_alloca(&status); // alloca allocates once per function, does not need a free
// Debugprintf("Waiting for complete. Avail %d Max %d last %d", avail, MaxAvail, lastavail);
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);
if (res != SND_PCM_STATE_RUNNING || lastavail == avail) // If sound system is not running then it needs data
// if (res != SND_PCM_STATE_RUNNING) // If sound system is not running then it needs data
// if (MaxAvail - avail < 100)
{
// Send complete - Restart Capture
OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, 0);
break;
}
lastavail = avail;
usleep(50000);
2023-09-04 19:06:44 +01:00
}
2024-07-23 21:26:30 +01:00
2023-09-04 19:06:44 +01:00
}
// 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
}
else if (SoundMode == 1)
{
oss_flush();
}
else if (SoundMode == 2)
{
pulse_flush();
}
SoundIsPlaying = FALSE;
Number = 0;
memset(buffer, 0, sizeof(buffer));
DMABuffer = &buffer[0][0];
#ifdef TXSILENCE
SendtoCard(&buffer[0][0], 1200); // Start sending silence (attempt to fix CM delay issue)
#endif
StartCapture();
return;
}
#ifdef TXSILENCE
// send silence (attempt to fix CM delay issue)
void SendSilence()
{
short buffer[2400];
snd_pcm_sframes_t Avail = snd_pcm_avail_update(playhandle);
if ((MaxAvail - Avail) < 1200)
{
// Keep at least 100 ms of audio in buffer
// printtick("Silence");
memset(buffer, 0, sizeof(buffer));
SendtoCard(buffer, 1200); // Start sending silence (attempt to fix CM delay issue)
}
}
#endif
// GPIO access stuff for PTT on PI
#ifdef __ARM_ARCH
/*
tiny_gpio.c
2016-04-30
Public Domain
*/
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#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)) | (mode<<shift);
}
int gpioGetMode(unsigned gpio)
{
int reg, shift;
reg = gpio/10;
shift = (gpio%10) * 3;
return (*(gpioReg + reg) >> 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<<x) will be set if gpio x is high. */
uint32_t gpioReadBank1(void) { return (*(gpioReg + GPLEV0)); }
uint32_t gpioReadBank2(void) { return (*(gpioReg + GPLEV1)); }
/* To clear gpio x bit or in (1<<x). */
void gpioClearBank1(uint32_t bits) { *(gpioReg + GPCLR0) = bits; }
void gpioClearBank2(uint32_t bits) { *(gpioReg + GPCLR1) = bits; }
/* To set gpio x bit or in (1<<x). */
void gpioSetBank1(uint32_t bits) { *(gpioReg + GPSET0) = bits; }
void gpioSetBank2(uint32_t bits) { *(gpioReg + GPSET1) = bits; }
unsigned gpioHardwareRevision(void)
{
static unsigned rev = 0;
FILE * filp;
char buf[512];
char term;
int chars=4; /* number of chars in revision string */
if (rev) return rev;
piModel = 0;
filp = fopen ("/proc/cpuinfo", "r");
if (filp != NULL)
{
while (fgets(buf, sizeof(buf), filp) != NULL)
{
if (piModel == 0)
{
if (!strncasecmp("model name", buf, 10))
{
if (strstr (buf, "ARMv6") != NULL)
{
piModel = 1;
chars = 4;
}
else if (strstr (buf, "ARMv7") != NULL)
{
piModel = 2;
chars = 6;
}
else if (strstr (buf, "ARMv8") != NULL)
{
piModel = 2;
chars = 6;
}
}
}
if (!strncasecmp("revision", buf, 8))
{
if (sscanf(buf+strlen(buf)-(chars+1),
"%x%c", &rev, &term) == 2)
{
if (term != '\n') rev = 0;
}
}
}
fclose(filp);
}
return rev;
}
int gpioInitialise(void)
{
int fd;
piRev = gpioHardwareRevision(); /* sets piModel and piRev */
fd = open("/dev/gpiomem", O_RDWR | O_SYNC) ;
if (fd < 0)
{
fprintf(stderr, "failed to open /dev/gpiomem\n");
return -1;
}
gpioReg = (uint32_t *)mmap(NULL, 0xB4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
if (gpioReg == MAP_FAILED)
{
fprintf(stderr, "Bad, mmap failed\n");
return -1;
}
return 0;
}
#endif
char Leds[8]= {0};
unsigned int PKTLEDTimer = 0;
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}
};
VOID COMSetDTR(HANDLE fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status |= TIOCM_DTR;
ioctl(fd, TIOCMSET, &status);
}
VOID COMClearDTR(HANDLE fd)
{
int status;
ioctl(fd, TIOCMGET, &status);
status &= ~TIOCM_DTR;
ioctl(fd, TIOCMSET, &status);
}
VOID COMSetRTS(HANDLE fd)
{
int status;
if (ioctl(fd, TIOCMGET, &status) == -1)
perror("COMSetRTS PTT TIOCMGET");
status |= TIOCM_RTS;
if (ioctl(fd, TIOCMSET, &status) == -1)
perror("COMSetRTS PTT TIOCMSET");
}
VOID COMClearRTS(HANDLE fd)
{
int status;
if (ioctl(fd, TIOCMGET, &status) == -1)
perror("COMClearRTS PTT TIOCMGET");
status &= ~TIOCM_RTS;
if (ioctl(fd, TIOCMSET, &status) == -1)
perror("COMClearRTS PTT TIOCMSET");
}
HANDLE OpenCOMPort(char * Port, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits)
{
2024-07-23 21:26:30 +01:00
char buf[256];
2023-09-04 19:06:44 +01:00
// Linux Version.
int fd;
u_long param = 1;
struct termios term;
struct speed_struct *s;
char fulldev[80];
UNUSED(Stopbits);
sprintf(fulldev, "/dev/%s", Port);
printf("%s\n", fulldev);
if ((fd = open(fulldev, O_RDWR | O_NDELAY)) == -1)
{
if (Quiet == 0)
{
perror("Com Open Failed");
sprintf(buf, " %s could not be opened", (char *)fulldev);
Debugprintf(buf);
}
return 0;
}
// Validate Speed Param
for (s = speed_table; s->user_speed != -1; s++)
if (s->user_speed == speed)
break;
if (s->user_speed == -1)
{
fprintf(stderr, "tty_speed: invalid speed %d", speed);
return FALSE;
}
if (tcgetattr(fd, &term) == -1)
{
perror("tty_speed: tcgetattr");
return FALSE;
}
cfmakeraw(&term);
cfsetispeed(&term, s->termios_speed);
cfsetospeed(&term, s->termios_speed);
if (tcsetattr(fd, TCSANOW, &term) == -1)
{
perror("tty_speed: tcsetattr");
return FALSE;
}
ioctl(fd, FIONBIO, &param);
Debugprintf("Port %s fd %d", fulldev, fd);
if (SetDTR)
COMSetDTR(fd);
else
COMClearDTR(fd);
if (SetRTS)
COMSetRTS(fd);
else
COMClearRTS(fd);
return fd;
}
BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite)
{
// Some systems seem to have a very small max write size
int ToSend = BytesToWrite;
int Sent = 0, ret;
while (ToSend)
{
ret = write(fd, &Block[Sent], ToSend);
if (ret >= ToSend)
return TRUE;
// perror("WriteCOM");
if (ret == -1)
{
if (errno != 11 && errno != 35) // Would Block
return FALSE;
usleep(10000);
ret = 0;
}
Sent += ret;
ToSend -= ret;
}
return TRUE;
}
VOID CloseCOMPort(HANDLE fd)
{
close(fd);
}
2024-07-23 21:26:30 +01:00
// "hints" processing for looking for SNOOP/MIX devices
int gethints()
{
const char *iface = "pcm";
void **hints;
char **n;
int err;
char hwdev[256];
snd_pcm_t *pcm = NULL;
char NameString[256];
CloseSoundCard();
Debugprintf("Available Mix/Snoop Devices\n");
PlaybackCount = 0;
CaptureCount = 0;
err = snd_device_name_hint(-1, iface, &hints);
if (err < 0)
Debugprintf("snd_device_name_hint error: %s", snd_strerror(err));
n = (char **)hints;
while (*n != NULL)
{
if (memcmp(*n, "NAMEmix", 7) == 0) //NAMEmix00|DESCQtSM Mix for hw0:0
{
char Hint[256];
char * ptr;
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
strcpy(Hint, *n);
ptr = strchr(Hint, '|');
if (ptr)
{
*ptr++ = 0;
}
strcpy(hwdev, &Hint[4]);
err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK);
if (err)
{
Debugprintf("Error %d opening output device %s ", err, hwdev);
goto nextdevice;
}
// Add device to list
if (ptr)
sprintf(NameString, "%s %s", hwdev, &ptr[4]);
else
strcpy(NameString, hwdev);
Debugprintf(NameString);
strcpy(PlaybackNames[PlaybackCount++], NameString);
snd_pcm_close(pcm);
pcm = NULL;
}
else if (memcmp(*n, "NAMEsnoop", 9) == 0)
{
char Hint[256];
char * ptr;
snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;
strcpy(Hint, *n);
ptr = strchr(Hint, '|');
if (ptr)
{
*ptr++ = 0;
}
strcpy(hwdev, &Hint[4]);
err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK);
if (err)
{
Debugprintf("Error %d opening input device %s ", err, hwdev);
goto nextdevice;
}
// Add device to list
if (ptr)
sprintf(NameString, "%s %s", hwdev, &ptr[4]);
else
strcpy(NameString, hwdev);
Debugprintf(NameString);
strcpy(CaptureNames[CaptureCount++], NameString);
snd_pcm_close(pcm);
pcm = NULL;
}
nextdevice:
n++;
}
snd_device_name_free_hint(hints);
return 0;
}
2024-10-29 22:45:21 +00:00
// Microsoft routines not available in gcc
int memicmp(unsigned char *a, unsigned char *b, int n)
{
if (n)
{
while (n && (toupper(*a) == toupper(*b)))
n--, a++, b++;
if (n)
return toupper(*a) - toupper(*b);
}
return 0;
}
int stricmp(const unsigned char * pStr1, const unsigned char *pStr2)
{
unsigned char c1, c2;
int v;
if (pStr1 == NULL)
{
if (pStr2)
Debugprintf("stricmp called with NULL 1st param - 2nd %s ", pStr2);
else
Debugprintf("stricmp called with two NULL params");
return 1;
}
do {
c1 = *pStr1++;
c2 = *pStr2++;
/* The casts are necessary when pStr1 is shorter & char is signed */
v = tolower(c1) - tolower(c2);
} while ((v == 0) && (c1 != '\0') && (c2 != '\0'));
return v;
}
char * strupr(char* s)
{
char* p = s;
if (s == 0)
return 0;
while (*p = toupper(*p)) p++;
return s;
}
char * strlwr(char* s)
{
char* p = s;
while (*p = tolower(*p)) p++;
return s;
}
2024-07-23 21:26:30 +01:00