/* 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 */ // UZ7HO Soundmodem Port by John Wiseman G8BPQ #include #include "QtSoundModem.h" #include "UZ7HOStuff.h" #include #define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt)) QList _MgmtSockets; QList _KISSSockets; QList _AGWSockets; QTcpServer * _KISSserver; QTcpServer * _AGWserver; QTcpServer * SixPackServer; QTcpSocket *SixPackSocket; QTcpServer * _MgmtServer; TMgmtMode ** MgmtConnections = NULL; int MgmtConCount = 0; extern workerThread *t; extern mynet m1; extern serialThread *serial; QString Response; extern int MgmtPort; extern "C" int KISSPort; extern "C" void * initPulse(); extern "C" int SoundMode; extern "C" int UDPClientPort; extern "C" int UDPServerPort; extern "C" int TXPort; extern char SixPackDevice[256]; extern int SixPackPort; extern int SixPackEnable; char UDPHost[64] = ""; int UDPServ = 0; // UDP Server Active (ie broadcast sound frams as UDP packets) QMutex mutex; extern void saveSettings(); extern int Closing; // Set to stop background thread extern "C" { void KISSDataReceived(void * sender, char * data, int length); void AGW_explode_frame(void * soket, char * data, int len); void KISS_add_stream(void * Socket); void KISS_del_socket(void * Socket); void AGW_add_socket(void * Socket); void AGW_del_socket(void * socket); void Debugprintf(const char * format, ...); int InitSound(BOOL Report); void soundMain(); void MainLoop(); void set_speed(int snd_ch, int Modem); void init_speed(int snd_ch); } void Process6PackData(unsigned char * Bytes, int Len); extern "C" int nonGUIMode; QTimer *timer; QTimer *timercopy; void mynet::start() { if (SoundMode == 3) OpenUDP(); if (UDPServ) OpenUDP(); if (KISSServ) { _KISSserver = new(QTcpServer); if (_KISSserver->listen(QHostAddress::Any, KISSPort)) connect(_KISSserver, SIGNAL(newConnection()), this, SLOT(onKISSConnection())); else { if (nonGUIMode) Debugprintf("Listen failed for KISS Port"); else { QMessageBox msgBox; msgBox.setText("Listen failed for KISS Port."); msgBox.exec(); } } } if (AGWServ) { _AGWserver = new(QTcpServer); if (_AGWserver->listen(QHostAddress::Any, AGWPort)) connect(_AGWserver, SIGNAL(newConnection()), this, SLOT(onAGWConnection())); else { if (nonGUIMode) Debugprintf("Listen failed for AGW Port"); else { QMessageBox msgBox; msgBox.setText("Listen failed for AGW Port."); msgBox.exec(); } } } if (MgmtPort) { _MgmtServer = new(QTcpServer); if (_MgmtServer->listen(QHostAddress::Any, MgmtPort)) connect(_MgmtServer, SIGNAL(newConnection()), this, SLOT(onMgmtConnection())); else { if (nonGUIMode) Debugprintf("Listen failed for Mgmt Port"); else { QMessageBox msgBox; msgBox.setText("Listen failed for Mgmt Port."); msgBox.exec(); } } } QObject::connect(t, SIGNAL(sendtoKISS(void *, unsigned char *, int)), this, SLOT(sendtoKISS(void *, unsigned char *, int)), Qt::QueuedConnection); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); timer->start(100); if (SixPackEnable) { if (SixPackDevice[0] && strcmp(SixPackDevice, "None") != 0) // Using serial { serial->startSlave(SixPackDevice, 30000, Response); serial->start(); // connect(serial, &serialThread::request, this, &QtSoundModem::showRequest); // connect(serial, &serialThread::error, this, &QtSoundModem::processError); // connect(serial, &serialThread::timeout, this, &QtSoundModem::processTimeout); } else if (SixPackPort) // using TCP { SixPackServer = new(QTcpServer); if (SixPackServer->listen(QHostAddress::Any, SixPackPort)) connect(SixPackServer, SIGNAL(newConnection()), this, SLOT(on6PackConnection())); } } } void mynet::MyTimerSlot() { // 100 mS Timer Event TimerEvent = TIMER_EVENT_ON; } void mynet::onAGWConnection() { QTcpSocket *clientSocket = _AGWserver->nextPendingConnection(); connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onAGWReadyRead())); connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onAGWSocketStateChanged(QAbstractSocket::SocketState))); _AGWSockets.push_back(clientSocket); AGW_add_socket(clientSocket); Debugprintf("AGW Connect Sock %x", clientSocket); } void mynet::onAGWSocketStateChanged(QAbstractSocket::SocketState socketState) { if (socketState == QAbstractSocket::UnconnectedState) { QTcpSocket* sender = static_cast(QObject::sender()); AGW_del_socket(sender); _AGWSockets.removeOne(sender); } } void mynet::onAGWReadyRead() { QTcpSocket* sender = static_cast(QObject::sender()); QByteArray datas = sender->readAll(); AGW_explode_frame(sender, datas.data(), datas.length()); } void mynet::on6PackReadyRead() { QTcpSocket* sender = static_cast(QObject::sender()); QByteArray datas = sender->readAll(); Process6PackData((unsigned char *)datas.data(), datas.length()); } void mynet::on6PackSocketStateChanged(QAbstractSocket::SocketState socketState) { if (socketState == QAbstractSocket::UnconnectedState) { QTcpSocket* sender = static_cast(QObject::sender()); } } void mynet::on6PackConnection() { QTcpSocket *clientSocket = SixPackServer->nextPendingConnection(); connect(clientSocket, SIGNAL(readyRead()), this, SLOT(on6PackReadyRead())); connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on6PackSocketStateChanged(QAbstractSocket::SocketState))); Debugprintf("6Pack Connect Sock %x", clientSocket); } void mynet::onKISSConnection() { QTcpSocket *clientSocket = _KISSserver->nextPendingConnection(); connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onKISSReadyRead())); connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onKISSSocketStateChanged(QAbstractSocket::SocketState))); _KISSSockets.push_back(clientSocket); KISS_add_stream(clientSocket); Debugprintf("KISS Connect Sock %x", clientSocket); } void Mgmt_del_socket(void * socket) { int i; TMgmtMode * MGMT = NULL; if (MgmtConCount == 0) return; for (i = 0; i < MgmtConCount; i++) { if (MgmtConnections[i]->Socket == socket) { MGMT = MgmtConnections[i]; break; } } if (MGMT == NULL) return; // Need to remove entry and move others down MgmtConCount--; while (i < MgmtConCount) { MgmtConnections[i] = MgmtConnections[i + 1]; i++; } } void mynet::onMgmtConnection() { QTcpSocket *clientSocket = (QTcpSocket *)_MgmtServer->nextPendingConnection(); connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onMgmtReadyRead())); connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onMgmtSocketStateChanged(QAbstractSocket::SocketState))); _MgmtSockets.append(clientSocket); // Create a data structure to hold session info TMgmtMode * MGMT; MgmtConnections = (TMgmtMode **)realloc(MgmtConnections, (MgmtConCount + 1) * sizeof(void *)); MGMT = MgmtConnections[MgmtConCount++] = (TMgmtMode *)malloc(sizeof(*MGMT)); memset(MGMT, 0, sizeof(*MGMT)); MGMT->Socket = clientSocket; Debugprintf("Mgmt Connect Sock %x", clientSocket); clientSocket->write("Connected to QtSM\r"); } void mynet::onMgmtSocketStateChanged(QAbstractSocket::SocketState socketState) { if (socketState == QAbstractSocket::UnconnectedState) { QTcpSocket* sender = static_cast(QObject::sender()); Mgmt_del_socket(sender); // free(sender->Msg); _MgmtSockets.removeOne(sender); Debugprintf("Mgmt Disconnect Sock %x", sender); } } void mynet::onMgmtReadyRead() { QTcpSocket* sender = static_cast(QObject::sender()); MgmtProcessLine(sender); } extern "C" void SendMgmtPTT(int snd_ch, int PTTState) { // Won't work in non=gui mode emit m1.mgmtSetPTT(snd_ch, PTTState); } extern "C" char * strlop(char * buf, char delim); extern "C" char modes_name[modes_count][21]; extern "C" int speed[5]; #ifndef WIN32 extern "C" int memicmp(char *a, char *b, int n); #endif void mynet::MgmtProcessLine(QTcpSocket* socket) { // Find Session TMgmtMode * MGMT = NULL; for (int i = 0; i < MgmtConCount; i++) { if (MgmtConnections[i]->Socket == socket) { MGMT = MgmtConnections[i]; break; } } if (MGMT == NULL) { socket->readAll(); return; } while (socket->bytesAvailable()) { QByteArray datas = socket->peek(512); char * Line = datas.data(); if (strchr(Line, '\r') == 0) return; char * rest = strlop(Line, '\r'); int used = rest - Line; // Read the upto the cr Null datas = socket->read(used); Line = datas.data(); rest = strlop(Line, '\r'); if (memicmp(Line, "QtSMPort ", 8) == 0) { if (strlen(Line) > 10) { int port = atoi(&Line[8]); int bpqport = atoi(&Line[10]); if ((port > 0 && port < 5) && (bpqport > 0 && bpqport < 64)) { MGMT->BPQPort[port - 1] = bpqport; socket->write("Ok\r"); } } } else if (memicmp(Line, "Modem ", 6) == 0) { int port = atoi(&Line[6]); if (port > 0 && port < 5) { // if any more params - if a name follows, set it else return it char reply[80]; sprintf(reply, "Port %d Chan %d Freq %d Modem %s \r", MGMT->BPQPort[port - 1], port, rx_freq[port - 1], modes_name[speed[port - 1]]); socket->write(reply); } else socket->write("Invalid Port\r"); } else { socket->write("Invalid command\r"); } } } void mynet::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState) { if (socketState == QAbstractSocket::UnconnectedState) { QTcpSocket* sender = static_cast(QObject::sender()); KISS_del_socket(sender); _KISSSockets.removeOne(sender); Debugprintf("KISS Disconnect Sock %x", sender); } } void mynet::onKISSReadyRead() { QTcpSocket* sender = static_cast(QObject::sender()); QByteArray datas = sender->readAll(); KISSDataReceived(sender, datas.data(), datas.length()); } void mynet::displayError(QAbstractSocket::SocketError socketError) { if (socketError == QTcpSocket::RemoteHostClosedError) return; qDebug() << tcpClient->errorString(); tcpClient->close(); tcpServer->close(); } void mynet::sendtoKISS(void * sock, unsigned char * Msg, int Len) { if (sock == NULL) { for (QTcpSocket* socket : _KISSSockets) { socket->write((char *)Msg, Len); } } else { QTcpSocket* socket = (QTcpSocket*)sock; socket->write((char *)Msg, Len); } free(Msg); } QTcpSocket * HAMLIBsock; int HAMLIBConnected = 0; int HAMLIBConnecting = 0; QTcpSocket * FLRIGsock; int FLRIGConnected = 0; int FLRIGConnecting = 0; void mynet::HAMLIBdisplayError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break; case QAbstractSocket::HostNotFoundError: if (nonGUIMode) qDebug() << "HAMLIB host was not found. Please check the host name and port settings."; else { QMessageBox::information(nullptr, tr("QtSM"), tr("HAMLIB host was not found. Please check the " "host name and port settings.")); } break; case QAbstractSocket::ConnectionRefusedError: qDebug() << "HAMLIB Connection Refused"; break; default: qDebug() << "HAMLIB Connection Failed"; break; } HAMLIBConnecting = 0; HAMLIBConnected = 0; } void mynet::HAMLIBreadyRead() { unsigned char Buffer[4096]; QTcpSocket* Socket = static_cast(QObject::sender()); // read the data from the socket. Don't do anyhing with it at the moment Socket->read((char *)Buffer, 4095); } void mynet::onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState) { if (socketState == QAbstractSocket::UnconnectedState) { // Close any connections HAMLIBConnected = 0; qDebug() << "HAMLIB Connection Closed"; } else if (socketState == QAbstractSocket::ConnectedState) { HAMLIBConnected = 1; HAMLIBConnecting = 0; qDebug() << "HAMLIB Connected"; } } void mynet::ConnecttoHAMLIB() { delete(HAMLIBsock); HAMLIBConnected = 0; HAMLIBConnecting = 1; HAMLIBsock = new QTcpSocket(); connect(HAMLIBsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(HAMLIBdisplayError(QAbstractSocket::SocketError))); connect(HAMLIBsock, SIGNAL(readyRead()), this, SLOT(HAMLIBreadyRead())); connect(HAMLIBsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onHAMLIBSocketStateChanged(QAbstractSocket::SocketState))); HAMLIBsock->connectToHost(HamLibHost, HamLibPort); return; } extern "C" void HAMLIBSetPTT(int PTTState) { // Won't work in non=gui mode emit m1.HLSetPTT(PTTState); } extern "C" void FLRigSetPTT(int PTTState) { // Won't work in non=gui mode emit m1.FLRigSetPTT(PTTState); } QTcpSocket * FLRigsock; int FLRigConnected = 0; int FLRigConnecting = 0; void mynet::FLRigdisplayError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break; case QAbstractSocket::HostNotFoundError: QMessageBox::information(nullptr, tr("QtSM"), "FLRig host was not found. Please check the " "host name and portsettings->"); break; case QAbstractSocket::ConnectionRefusedError: qDebug() << "FLRig Connection Refused"; break; default: qDebug() << "FLRig Connection Failed"; break; } FLRigConnecting = 0; FLRigConnected = 0; } void mynet::FLRigreadyRead() { unsigned char Buffer[4096]; QTcpSocket* Socket = static_cast(QObject::sender()); // read the data from the socket. Don't do anyhing with it at the moment Socket->read((char *)Buffer, 4095); } void mynet::onFLRigSocketStateChanged(QAbstractSocket::SocketState socketState) { if (socketState == QAbstractSocket::UnconnectedState) { // Close any connections FLRigConnected = 0; FLRigConnecting = 0; // delete (FLRigsock); // FLRigsock = 0; qDebug() << "FLRig Connection Closed"; } else if (socketState == QAbstractSocket::ConnectedState) { FLRigConnected = 1; FLRigConnecting = 0; qDebug() << "FLRig Connected"; } } void mynet::ConnecttoFLRig() { delete(FLRigsock); FLRigConnected = 0; FLRigConnecting = 1; FLRigsock = new QTcpSocket(); connect(FLRigsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(FLRigdisplayError(QAbstractSocket::SocketError))); connect(FLRigsock, SIGNAL(readyRead()), this, SLOT(FLRigreadyRead())); connect(FLRigsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onFLRigSocketStateChanged(QAbstractSocket::SocketState))); FLRigsock->connectToHost(FLRigHost, FLRigPort); return; } static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" "User-Agent: XMLRPC++ 0.8\r\n" "Host: 127.0.0.1:7362\r\n" "Content-Type: text/xml\r\n" "Content-length: %d\r\n" "\r\n%s"; static char Req[] = "\r\n" "%s\r\n" "%s" "\r\n"; void mynet::doFLRigSetPTT(int c) { int Len; char ReqBuf[512]; char SendBuff[512]; char ValueString[256] = ""; sprintf(ValueString, "%d", c); Len = sprintf(ReqBuf, Req, "rig.set_ptt", ValueString); Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState) ConnecttoFLRig(); if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState) return; FLRigsock->write(SendBuff); FLRigsock->waitForBytesWritten(3000); QByteArray datas = FLRigsock->readAll(); qDebug(datas.data()); } extern "C" void startTimer(int Time) { // Won't work in non=gui mode emit m1.startTimer(Time); } void mynet::dostartTimer(int Time) { timercopy->start(Time); } extern "C" void stopTimer() { // Won't work in non=gui mode emit m1.stopTimer(); } void mynet::dostopTimer() { timercopy->stop(); } void mynet::doHLSetPTT(int c) { char Msg[16]; if (HAMLIBsock == nullptr || HAMLIBsock->state() != QAbstractSocket::ConnectedState) ConnecttoHAMLIB(); sprintf(Msg, "T %d\r\n", c); HAMLIBsock->write(Msg); HAMLIBsock->waitForBytesWritten(30000); QByteArray datas = HAMLIBsock->readAll(); qDebug(datas.data()); } void mynet::domgmtSetPTT(int snd_ch, int PTTState) { char Msg[64]; uint64_t ret; for (QTcpSocket* socket : _MgmtSockets) { // Find Session TMgmtMode * MGMT = NULL; for (int i = 0; i < MgmtConCount; i++) { if (MgmtConnections[i]->Socket == socket) { MGMT = MgmtConnections[i]; break; } } if (MGMT == NULL) continue; if (MGMT->BPQPort[snd_ch]) { sprintf(Msg, "PTT %d %s\r", MGMT->BPQPort[snd_ch], PTTState ? "ON" : "OFF"); ret = socket->write(Msg); } } } extern "C" void KISSSendtoServer(void * sock, Byte * Msg, int Len) { emit t->sendtoKISS(sock, Msg, Len); } void workerThread::run() { if (SoundMode == 2) // Pulse { if (initPulse() == nullptr) { if (nonGUIMode) { qDebug() << "PulseAudio requested but pulseaudio libraries not found\nMode set to ALSA\n"; } else { QMessageBox msgBox; msgBox.setText("PulseAudio requested but pulseaudio libraries not found\nMode set to ALSA"); msgBox.exec(); } SoundMode = 0; saveSettings(); } } soundMain(); if (SoundMode != 3) { if (!InitSound(1)) { // QMessageBox msgBox; // msgBox.setText("Open Sound Card Failed"); // msgBox.exec(); } } // Initialise Modems init_speed(0); init_speed(1); init_speed(2); init_speed(3); // emit t->openSockets(); while (Closing == 0) { // Run scheduling loop MainLoop(); this->msleep(10); } qDebug() << "Saving Settings"; saveSettings(); qDebug() << "Main Loop exited"; qApp->exit(); }; // Audio over UDP Code. // Code can either send audio blocks from the sound card as UDP packets or use UDP packets from // a suitable source (maybe another copy of QtSM) and process them instead of input frm a sound card/ // ie act as a server or client for UDP audio. // of course we need bidirectional audio, so even when we are a client we send modem generated samples // to the server and as a server pass received smaples to modem // It isn't logical to run as both client and server, so probably can use just one socket QUdpSocket * udpSocket; qint64 udpServerSeqno= 0; qint64 udpClientLastSeq = 0; qint64 udpServerLastSeq = 0; int droppedPackets = 0; extern "C" int UDPSoundIsPlaying; QQueue queue; void mynet::OpenUDP() { udpSocket = new QUdpSocket(); if (UDPServ) { udpSocket->bind(QHostAddress("0.0.0.0"), UDPServerPort); QTimer *timer = new QTimer(this); timercopy = timer; connect(timer, SIGNAL(timeout()), this, SLOT(dropPTT())); } else udpSocket->bind(QHostAddress("0.0.0.0"), UDPClientPort); connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); } extern "C" void Flush(); void mynet::dropPTT() { timercopy->stop(); if (UDPSoundIsPlaying) { // Drop PTT when all sent Flush(); UDPSoundIsPlaying = 0; Debugprintf("PTT Off"); RadioPTT(0, 0); } } void mynet::readPendingDatagrams() { while (udpSocket->hasPendingDatagrams()) { QHostAddress Addr; quint16 rxPort; char copy[1501]; // We expect datagrams of 1040 bytes containing a 16 byte header and 512 16 bit samples // We should get a datagram every 43 mS. We need to use a timeout to drop PTT if running as server if (UDPServ) timercopy->start(200); int Len = udpSocket->readDatagram(copy, 1500, &Addr, &rxPort); if (Len == 1040) { qint64 Seq; memcpy(&Seq, copy, sizeof(udpServerSeqno)); if (Seq < udpClientLastSeq || udpClientLastSeq == 0) // Client or Server Restarted udpClientLastSeq = Seq; else { int Missed = Seq - udpClientLastSeq; if (Missed > 100) // probably stopped in debug Missed = 1; while (--Missed) { droppedPackets++; // insert silence to maintain timing unsigned char * pkt = (unsigned char *)malloc(1024); memset(pkt, 0, 1024); mutex.lock(); queue.append(pkt); mutex.unlock(); } } udpClientLastSeq = Seq; unsigned char * pkt = (unsigned char *)malloc(1024); memcpy(pkt, ©[16], 1024); mutex.lock(); queue.append(pkt); mutex.unlock(); } } } void mynet::socketError() { char errMsg[80]; sprintf(errMsg, "%d %s", udpSocket->state(), udpSocket->errorString().toLocal8Bit().constData()); // qDebug() << errMsg; // QMessageBox::question(NULL, "ARDOP GUI", errMsg, QMessageBox::Yes | QMessageBox::No); } extern "C" void sendSamplestoStdout(short * Samples, int nSamples) { } extern "C" void sendSamplestoUDP(short * Samples, int nSamples, int Port) { if (udpSocket == nullptr) return; unsigned char txBuff[1048]; memcpy(txBuff, &udpServerSeqno, sizeof(udpServerSeqno)); udpServerSeqno++; if (nSamples > 512) nSamples = 512; nSamples <<= 1; // short to byte memcpy(&txBuff[16], Samples, nSamples); udpSocket->writeDatagram((char *)txBuff, nSamples + 16, QHostAddress(UDPHost), Port); } static int min = 0, max = 0, lastlevelGUI = 0, lastlevelreport = 0; static UCHAR CurrentLevel = 0; // Peak from current samples extern "C" int SoundIsPlaying; extern "C" short * SendtoCard(short * buf, int n); extern "C" short * DMABuffer; extern "C" void ProcessNewSamples(short * Samples, int nSamples); extern "C" void UDPPollReceivedSamples() { if (queue.isEmpty()) return; short * ptr; short * save; // If we are using UDP for output (sound server) send samples to sound card. // If for input (virtual sound card) then pass to modem if (UDPServ) { // We only get packets if TX active (sound VOX) so raise PTT and start sending // ?? do we ignore if local modem is already sending ?? if (SoundIsPlaying) { mutex.lock(); save = ptr = (short *)queue.dequeue(); mutex.unlock(); free(save); } if (UDPSoundIsPlaying == 0) { // Wait for a couple of packets to reduce risk of underrun (but not too many or delay will be excessive if (queue.count() < 3) return; UDPSoundIsPlaying = 1; Debugprintf("PTT On"); RadioPTT(0, 1); // UDP only use one channel /// !! how do we drop ptt ?? } while (queue.count() > 1) { short * outptr = DMABuffer; boolean dropPTT1 = 1; boolean dropPTT2 = 1; // We get mono samples but soundcard expects stereo // Sound card needs 1024 samples so send two packets mutex.lock(); save = ptr = (short *)queue.dequeue(); mutex.unlock(); for (int n = 0; n < 512; n++) { *(outptr++) = *ptr; *(outptr++) = *ptr++; // Duplicate if (*ptr) dropPTT1 = 0; // Drop PTT if all samples zero } free(save); mutex.lock(); save = ptr = (short *)queue.dequeue(); mutex.unlock(); for (int n = 0; n < 512; n++) { *(outptr++) = *ptr; *(outptr++) = *ptr++; // Duplicate if (*ptr) dropPTT2 = 0; // Drop PTT if all samples zero } free(save); if (dropPTT1 && dropPTT2) { startTimer(1); // All zeros so no need to send return; } DMABuffer = SendtoCard(DMABuffer, 1024); if (dropPTT2) // 2nd all zeros startTimer(1); } return; } mutex.lock(); save = ptr = (short *)queue.dequeue(); mutex.unlock(); // We get mono samples but modem expects stereo short Buff[2048]; short * inptr = (short *)ptr; short * outptr = Buff; 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; if ((Now - lastlevelreport) > 10000) // 10 Secs { char HostCmd[64]; lastlevelreport = Now; sprintf(HostCmd, "INPUTPEAKS %d %d", min, max); Debugprintf("Input peaks = %d, %d", min, max); } min = max = 0; } for (int n = 0; n < 512; n++) { *(outptr++) = *inptr; *(outptr++) = *inptr++; // Duplicate } ProcessNewSamples(Buff, 512); free(save); }