/** * Copyright (c) 2017 - 2022 by Ambient Noise, LLC. * * This software is copyrighted by and is the sole property of Ambient Noise, LLC. * All rights, title, ownership, or other interests in the software remain the * property of Ambient Noise, LLC. * This software may only be used in accordance with the corresponding license agreement. * Any unauthorized use, duplication, transmission, distribution, or disclosure of * this software is expressly forbidden. * * This Copyright notice may not be removed or modified without prior * written consent of Ambient Noise, LLC. * * Ambient Noise, LLC. reserves the right to modify this software without notice. * *****************************************************************************************/ /** * @file HostTalker.cpp * @author Yair Raz * @brief The Meridian Audio Engine Audio TCP/IP host interface implementation * file. * * This file is based on a C example created by DSP Concepts, Inc. */ #include "HostTalker.h" #include "Errors.h" #include "ProxyIDs.h" #include #include namespace meridian { namespace audio { namespace MAE { /* * Constructor */ HostTalker::HostTalker(std::string ipAddress, uint32_t sendPort, int32_t tcpTimeout) : pTalker(new CTcpIOTalk), ipAddress(ipAddress), sendPort(sendPort), tcpTimeout(tcpTimeout) { } /* * Destructor. */ HostTalker::~HostTalker() { if (pTalker != nullptr) { delete pTalker; } } /* * isConnected() */ bool HostTalker::isConnected() { // To prevent disconnect while a transaction is in progress FastMutex::ScopedLock lock(_mutex); bool connected = false; if (pTalker != nullptr) { connected = (pTalker->IsConnected() == 1); } return connected; } /* * disconnect() */ void HostTalker::disconnect() { // To prevent disconnect while a transaction is in progress FastMutex::ScopedLock lock(_mutex); if (pTalker != nullptr) { pTalker->KillSocket(); } } // Compute checksum void HostTalker::computeCRC(uint32_t * pMessage) { uint32_t txLen = pMessage[0] >> 16; uint32_t csLen = txLen - 1u; uint32_t dwCS = 0; uint32_t i; for (i = 0u; i < csLen; ++i) { dwCS ^= pMessage[i]; } pMessage[csLen] = dwCS; } // Verify checksum uint32_t HostTalker::verifyChecksum(const uint32_t * pMessage) { uint32_t txLen = pMessage[0] >> 16; uint32_t dwCS = 0; uint32_t i; for (i = 0; i < txLen; i++) { dwCS ^= pMessage[i]; } return dwCS; } // Write an array without set. int32_t HostTalker::remoteSetValue(uint32_t endPointID, uint32_t handle, const void * value, uint32_t arrayOffset, uint32_t length) { FastMutex::ScopedLock lock(_mutex); int32_t ret; uint32_t txSize = 5 + length; uint32_t forCore = (endPointID << 4) | ((handle & 0x780) >> 7); if (txSize >= COMMAND_BUFFER_LENGTH) { // Length too big. return E_ARGUMENT_ERROR; } if (value == nullptr) { // Can't allow nullptr pointer. return E_ARGUMENT_ERROR; } if (handle & 0x80000000) { // Array case. sendBuffer[0] = (txSize << 16) | (forCore << 8) | PFID_SetValues; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = arrayOffset; sendBuffer[3] = length; memcpy(&sendBuffer[4], value, length * sizeof(uint32_t)); computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } } else { // Scalar case. sendBuffer[0] = (5 << 16) | (forCore << 8) | PFID_SetValue; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = *(static_cast(value)); sendBuffer[3] = 0; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } } return ret; } // Write array with set call. int32_t HostTalker::remoteSetValueMask(uint32_t endPointID, uint32_t handle, const void * value, uint32_t arrayOffset, uint32_t length, uint32_t mask) { FastMutex::ScopedLock lock(_mutex); uint32_t txSize = 6 + length; int32_t ret; uint32_t forCore = (endPointID << 4) | ((handle & 0x780) >> 7); if (txSize >= COMMAND_BUFFER_LENGTH) { // Length too big. return E_ARGUMENT_ERROR; } if (value == nullptr) { // Can't allow nullptr pointer. return E_ARGUMENT_ERROR; } if (handle & 0x80000000) { // Array case. sendBuffer[0] = (txSize << 16) | (forCore << 8) | PFID_SetValuesSetCall; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = arrayOffset; sendBuffer[3] = mask; sendBuffer[4] = length; memcpy(&sendBuffer[5], value, length * sizeof(uint32_t)); computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } } else { // Scalar case. sendBuffer[0] = (6 << 16) | (forCore << 8) | PFID_SetValueSetCall; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = *(static_cast(value)); sendBuffer[3] = mask; sendBuffer[4] = 0; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } } return ret; } // Read an array value without set int32_t HostTalker::remoteGetValue(uint32_t endPointID, uint32_t handle, void * value, int32_t arrayOffset, uint32_t length) { FastMutex::ScopedLock lock(_mutex); int32_t ret; uint32_t forCore = (endPointID << 4) | ((handle & 0x780) >> 7); if (length + 3 > COMMAND_BUFFER_LENGTH) { // Too big. return E_ARGUMENT_ERROR; } if (value == nullptr) { // Can't allow nullptr pointer. return E_ARGUMENT_ERROR; } if (handle & 0x80000000) { // Array case. sendBuffer[0] = (5 << 16) | (forCore << 8) | PFID_FetchValues; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = static_cast(arrayOffset); sendBuffer[3] = length; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); if (ret == E_SUCCESS) { memcpy(value, &recvBuffer[2], length * sizeof(uint32_t)); } } } else { // Scalar case. sendBuffer[0] = (4 << 16) | (forCore << 8) | PFID_FetchValue; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = 0; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); if (ret == E_SUCCESS) { *(static_cast(value)) = recvBuffer[2]; } } } return ret; } // Read an array value with set int32_t HostTalker::remoteGetValueMask(uint32_t endPointID, uint32_t handle, void * value, int32_t arrayOffset, uint32_t length, uint32_t mask) { FastMutex::ScopedLock lock(_mutex); int32_t ret; uint32_t forCore = (endPointID << 4) | ((handle & 0x780) >> 7); if (length + 3 > COMMAND_BUFFER_LENGTH) { // Too big. return E_ARGUMENT_ERROR; } if (value == nullptr) { // Can't allow nullptr pointer. return E_ARGUMENT_ERROR; } if (handle & 0x80000000) { // Array case. sendBuffer[0] = (6 << 16) | (forCore << 8) | PFID_GetCallFetchValues; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = static_cast(arrayOffset); sendBuffer[3] = mask; sendBuffer[4] = length; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); if (ret == E_SUCCESS) { memcpy(value, &recvBuffer[2], length * sizeof(uint32_t)); } } } else { // Scalar case. sendBuffer[0] = (5 << 16) | (forCore << 8) | PFID_GetCallFetchValue; sendBuffer[1] = handle & 0x3ffff07f; sendBuffer[2] = mask; sendBuffer[3] = 0; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); if (ret == E_SUCCESS) { *(static_cast(value)) = recvBuffer[2]; } } } return ret; } // Set a module state int32_t HostTalker::remoteSetStatus(uint32_t endPointID, uint32_t handle, uint32_t status) { FastMutex::ScopedLock lock(_mutex); int32_t ret; uint32_t forCore = (endPointID << 4) | ((handle & 0x780) >> 7); sendBuffer[0] = (4 << 16) | (forCore << 8) | PFID_SetModuleState; sendBuffer[1] = (handle & 0x3ffff000) >> 12; sendBuffer[2] = status; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } return ret; } // Get the state of a module int32_t HostTalker::remoteGetStatus(uint32_t endPointID, uint32_t handle, uint32_t *pStatus) { FastMutex::ScopedLock lock(_mutex); int32_t ret; uint32_t forCore = (endPointID << 4) | ((handle & 0x780) >> 7); if (pStatus == nullptr) { // Can't allow nullptr pointer. return E_ARGUMENT_ERROR; } sendBuffer[0] = (3 << 16) | (forCore << 8) | PFID_GetModuleState; sendBuffer[1] = (handle & 0x3ffff000) >> 12; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { *pStatus = recvBuffer[1]; } return ret; } // Get a module class int32_t HostTalker::remoteGetModuleClass(uint32_t endPointID, uint32_t handle, uint32_t *pClassID) { FastMutex::ScopedLock lock(_mutex); int32_t ret; uint32_t forCore = (endPointID << 4) | ((handle & 0x780) >> 7); if (pClassID == nullptr) { // Can't allow nullptr pointer. return E_ARGUMENT_ERROR; } sendBuffer[0] = (3 << 16) | (forCore << 8) | PFID_GetObjectByID; sendBuffer[1] = (handle & 0x3ffff000) >> 12; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { *pClassID = recvBuffer[2]; } return ret; } // Stop audio. int32_t HostTalker::remoteStopAudio(uint32_t forCore) { FastMutex::ScopedLock lock(_mutex); int32_t ret; sendBuffer[0] = (2 << 16) | (forCore << 8) | PFID_StopAudio; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } return ret; } // Start audio. int32_t HostTalker::remoteStartAudio(uint32_t forCore) { FastMutex::ScopedLock lock(_mutex); int32_t ret; sendBuffer[0] = (2 << 16) | (forCore << 8) | PFID_StartAudio; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } return ret; } // Destroy. int32_t HostTalker::remoteDestroy(uint32_t forCore) { FastMutex::ScopedLock lock(_mutex); int32_t ret; sendBuffer[0] = (2 << 16) | (forCore << 8) | PFID_Destroy; computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } return ret; } // Load an AWB file to the target int32_t HostTalker::loadAwbFile(const char * filename, uint32_t * pPos) { FastMutex::ScopedLock lock(_mutex); int32_t ret = 0; FILE * fpin; uint32_t nFileOffset = 0; size_t nread; uint32_t len; if (filename == nullptr || pPos == nullptr) { // Can't allow nullptr pointer. return E_ARGUMENT_ERROR; } *pPos = 0; fpin = fopen(filename, "rb"); if (!fpin) { // Can't find file return E_NOSUCHFILE; } nread = fread(&sendBuffer[0], sizeof(uint32_t), 1, fpin); if (nread != 1) { // File read error. ret = E_IOERROR; goto done; } nFileOffset = 1; len = (sendBuffer[0] >> 16) & 0xffff; while (len) { if (len > COMMAND_BUFFER_LENGTH) { // Command too long. ret = E_OUT_OF_SPACE; goto done; } if (len > 2) { nread = fread(&sendBuffer[1], sizeof(uint32_t), len - 2, fpin); if (nread != len - 2) { // File error. ret = E_IOERROR; goto done; } nFileOffset += len - 2; } // Send the command. computeCRC(sendBuffer); ret = transact(sendBuffer, recvBuffer); if (ret != E_SUCCESS || (((recvBuffer[0] >> 16) >= 3) && ((recvBuffer[1] & 0xffff0000) != 0xbeef0000) && (recvBuffer[1] & 0x80000000U) != 0)) { // Command failed. if (ret == E_SUCCESS) { ret = static_cast(recvBuffer[1]); } printf("awe_loadAwbFile: failed with %d at %d\n", ret, nFileOffset); *pPos = nFileOffset; goto done; } nread = fread(&sendBuffer[0], sizeof(uint32_t), 1, fpin); if (nread == 0) { // End of file. break; } if (nread != 1) { // File error. ret = E_IOERROR; goto done; } nFileOffset++; len = (sendBuffer[0] >> 16) & 0xffff; } done: if (fpin) { fclose(fpin); } return ret; } } // namespace MAE } // namespace audio } // namespace meridian