diff --git a/Makefile.am b/Makefile.am index 4f1331d..251d003 100644 --- a/Makefile.am +++ b/Makefile.am @@ -33,6 +33,9 @@ cmSRC += src/libcm/cmAudioPort.c src/libcm/cmApBuf.c src/libcm/cmAudioAggDev.c s cmHDR += src/libcm/cmMidiFilePlay.h src/libcm/cmMidiPort.h src/libcm/cmMidiFile.h src/libcm/cmMidi.h cmSRC += src/libcm/cmMidiFilePlay.c src/libcm/cmMidiPort.c src/libcm/cmMidiFile.c src/libcm/cmMidi.c +cmHDR += src/libcm/cmSerialPort.h +cmSRC += src/libcm/cmSerialPort.c + cmHDR += src/libcm/cmAudioFile.h src/libcm/cmAudioFileMgr.h src/libcm/cmMsgProtocol.h src/libcm/cmAudioSys.h src/libcm/cmAudioPortFile.h src/libcm/cmAudioFileDev.h cmSRC += src/libcm/cmAudioFile.c src/libcm/cmAudioFileMgr.c src/libcm/cmMsgProtocol.c src/libcm/cmAudioSys.c src/libcm/cmAudioPortFile.c src/libcm/cmAudioFileDev.c diff --git a/cmApBuf.c b/cmApBuf.c index 0858d78..d034633 100644 --- a/cmApBuf.c +++ b/cmApBuf.c @@ -815,6 +815,7 @@ void cmApBufInputToOutput( unsigned iDevIdx, unsigned oDevIdx ) unsigned byteCnt = ip->dspFrameCnt * sizeof(cmApSample_t); + if( oBufPtrArray[i] != NULL ) { // the input channel is not disabled @@ -846,18 +847,20 @@ void cmApBufReport( cmRpt_t* rpt ) unsigned ii = 0; unsigned oi = 0; unsigned fn = 0; + cmApSample_t mtr = 0; for(k=0; kchCnt; ++k) { cmApCh* cp = ip->chArray + i; ii += cp->ii; oi += cp->oi; fn += cp->fn; + mtr += _cmApMeterValue(cp); } - cmRptPrintf(rpt,"%s - i:%7i o:%7i f:%7i n:%7i err %s:%7i ", + cmRptPrintf(rpt,"%s - i:%7i o:%7i f:%7i n:%7i err %s:%7i mtr:%5.4f ", j==0?"IN":"OUT", - ii,oi,fn,ip->n, (j==0?"over":"under"), ip->faultCnt); - + ii,oi,fn,ip->n, (j==0?"over":"under"), ip->faultCnt, mtr); + } cmRptPrintf(rpt,"\n"); diff --git a/cmAudDsp.c b/cmAudDsp.c index 654a1cc..85eded6 100644 --- a/cmAudDsp.c +++ b/cmAudDsp.c @@ -19,6 +19,7 @@ #include "cmApBuf.h" #include "cmMidi.h" #include "cmMidiPort.h" +#include "cmSerialPort.h" #include "dsp/cmDspValue.h" #include "cmMsgProtocol.h" #include "cmThread.h" @@ -91,6 +92,12 @@ typedef struct unsigned msgsPerClientPoll; const cmChar_t* dfltProgramLabel; + char* serialDeviceStr; + unsigned serialBaud; + unsigned serialCfgFlags; + unsigned serialPollPeriodMs; + cmSeH_t serialPortH; + cmAdAggDev_t* aggDevArray; unsigned aggDevCnt; @@ -169,6 +176,7 @@ cmAdRC_t _cmAdParseSysJsonTree( cmAd_t* p ) cmJsonNode_t* nrtDevArrNodePtr = NULL; cmJsonNode_t* afpDevArrNodePtr = NULL; cmJsonNode_t* audDspNodePtr = NULL; + cmJsonNode_t* serialNodePtr = NULL; const cmChar_t* errLabelPtr = NULL; unsigned i; cmJsRC_t jsRC = kOkJsRC; @@ -190,12 +198,28 @@ cmAdRC_t _cmAdParseSysJsonTree( cmAd_t* p ) "aggDevArray", kArrayTId | kOptArgJsFl, &aggDevArrNodePtr, "nrtDevArray", kArrayTId | kOptArgJsFl, &nrtDevArrNodePtr, "afpDevArray", kArrayTId | kOptArgJsFl, &afpDevArrNodePtr, + "serial", kObjectTId | kOptArgJsFl, &serialNodePtr, NULL )) != kOkJsRC ) { rc = _cmAdParseMemberErr(p, jsRC, errLabelPtr, "aud_dsp" ); goto errLabel; } + // parse the serial port cfg + if( serialNodePtr != NULL ) + { + if(( jsRC = cmJsonMemberValues( serialNodePtr, &errLabelPtr, + "device", kStringTId, &p->serialDeviceStr, + "baud", kIntTId, &p->serialBaud, + "flags", kIntTId, &p->serialCfgFlags, + "pollPeriodMs", kIntTId, &p->serialPollPeriodMs, + NULL )) != kOkJsRC ) + { + rc = _cmAdParseMemberErr(p, jsRC, errLabelPtr, "serial" ); + goto errLabel; + } + } + // parse the aggregate device specifications into p->aggDevArray[]. if( aggDevArrNodePtr != NULL && (p->aggDevCnt = cmJsonChildCount(aggDevArrNodePtr)) > 0) { @@ -382,6 +406,22 @@ cmAdRC_t _cmAdSetup( cmAd_t* p ) return rc; } +cmAdRC_t _cmAdCreateSerialPort( cmAd_t* p ) +{ + cmAdRC_t rc = kOkAdRC; + + if( p->serialDeviceStr != NULL ) + { + p->serialPortH = cmSeCreate( &p->ctx, &p->serialPortH, p->serialDeviceStr, p->serialBaud, p->serialCfgFlags, NULL, NULL, p->serialPollPeriodMs ); + + if( !cmSeIsOpen(p->serialPortH) ) + { + rc = cmErrMsg(&p->err,kSerialDevCreateFailAdRC,"The serial device '%s' creation failed.",cmStringNullGuard(p->serialDeviceStr)); + } + } + return rc; +} + cmAdRC_t _cmAdCreateAggDevices( cmAd_t* p ) { cmAdRC_t rc = kOkAdRC; @@ -570,6 +610,15 @@ cmAdRC_t _cmAudDspFree( cmAd_t* p ) goto errLabel; } + if( cmSeIsOpen(p->serialPortH) ) + { + if( cmSeDestroy(&p->serialPortH) != kOkSeRC ) + { + rc = cmErrMsg(&p->err,kSerialPortFailAdRC,"Serial port finalize failed."); + goto errLabel; + } + } + if( cmMpIsInitialized() ) if( cmMpFinalize() != kOkMpRC ) { @@ -695,6 +744,13 @@ cmAdRC_t cmAudDspAlloc( cmCtx_t* ctx, cmAdH_t* hp, cmMsgSendFuncPtr_t cbFunc, vo goto errLabel; } + // create the serial port + if( _cmAdCreateSerialPort(p) != kOkAdRC ) + { + rc = cmErrMsg(&p->err,kSerialPortFailAdRC,"The MIDI system initialization failed."); + goto errLabel; + } + // initialize the MIDI system if( cmMpInitialize(ctx,NULL,NULL,p->midiPortBufByteCnt,"app") != kOkMpRC ) { @@ -713,7 +769,7 @@ cmAdRC_t cmAudDspAlloc( cmCtx_t* ctx, cmAdH_t* hp, cmMsgSendFuncPtr_t cbFunc, vo goto errLabel; // initialize the DSP system - if( cmDspSysInitialize(ctx,&p->dsH,p->netH) ) + if( cmDspSysInitialize(ctx,&p->dsH,p->netH,p->serialPortH) ) { rc = cmErrMsg(&p->err,kDspSysFailAdRC,"The DSP system initialization failed."); goto errLabel; @@ -1143,7 +1199,7 @@ cmAdRC_t _cmAudDspLoadAudioSys( cmAd_t* p, unsigned asCfgIdx ) { // ... and allocate additional DSP systems when more than one sub-sys is // defined in the audio system configuration - if( cmDspSysInitialize(&p->ctx,&dsH,p->netH) != kOkDspRC ) + if( cmDspSysInitialize(&p->ctx,&dsH,p->netH,p->serialPortH) != kOkDspRC ) { rc = cmErrMsg(&p->err,kDspSysFailAdRC,"Unable to initialize an additional DSP system."); goto errLabel; diff --git a/cmAudDsp.h b/cmAudDsp.h index f33077f..152c94e 100644 --- a/cmAudDsp.h +++ b/cmAudDsp.h @@ -13,6 +13,7 @@ extern "C" { kAudioPortFailAdRC, kAudioSysFailAdRC, kMidiSysFailAdRC, + kSerialPortFailAdRC, kDspSysFailAdRC, kFileSysFailAdRC, kJsonFailAdRC, @@ -21,6 +22,7 @@ extern "C" { kNoPgmLoadedAdRC, kInvalidSubSysIdxAdRC, kUnknownMsgTypeAdRC, + kSerialDevCreateFailAdRC, kAggDevSysFailAdRC, kAggDevCreateFailAdRC, kNrtDevSysFailAdRC, diff --git a/cmAudioPort.c b/cmAudioPort.c index 8237887..ba79f9d 100644 --- a/cmAudioPort.c +++ b/cmAudioPort.c @@ -738,11 +738,13 @@ int cmApPortTest( bool runFl, cmRpt_t* rpt, int argc, const char* argv[] ) // setup the buffer for the output device cmApBufSetup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.outDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.outDevIdx,false), r.framesPerCycle ); - + // setup the buffer for the input device if( r.inDevIdx != r.outDevIdx ) cmApBufSetup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, cmApDeviceChannelCount(r.inDevIdx,true), r.framesPerCycle, cmApDeviceChannelCount(r.inDevIdx,false), r.framesPerCycle ); + cmApBufEnableMeter( r.inDevIdx, -1, kEnableApFl ); + // setup an output device if(cmApDeviceSetup(r.outDevIdx,r.srate,r.framesPerCycle,_cmApPortCb2,&r) != kOkApRC ) cmRptPrintf(rpt,"Out device setup failed.\n"); diff --git a/cmAudioSys.c b/cmAudioSys.c index 28c3761..d819ae6 100644 --- a/cmAudioSys.c +++ b/cmAudioSys.c @@ -15,6 +15,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmAudioSysMsg.h" #include "cmAudioSys.h" #include "cmMidi.h" @@ -37,6 +38,7 @@ typedef struct cmTsMp1cH_t htdQueueH; // host-to-dsp thread safe msg queue cmThreadMutexH_t engMutexH; // thread mutex and condition variable cmUdpNetH_t netH; + cmSeH_t serialPortH; bool enableFl; // application controlled pause flag bool runFl; // false during finalization otherwise true bool statusFl; // true if regular status notifications should be sent @@ -507,6 +509,13 @@ void _cmAudioSysMidiCallback( const cmMidiPacket_t* pktArray, unsigned pktCnt ) } +void _cmAudioSysSerialPortCallback( void* cbArg, const void* byteA, unsigned byteN ) +{ + //_cmAsCfg_t* p (_cmAsCfg_t*)cbArg; + + // TODO: handle serial receive +} + cmAsRC_t cmAudioSysAllocate( cmAudioSysH_t* hp, cmRpt_t* rpt, const cmAudioSysCfg_t* cfg ) { cmAsRC_t rc; @@ -832,6 +841,7 @@ cmAsRC_t cmAudioSysInitialize( cmAudioSysH_t h, const cmAudioSysCfg_t* cfg ) cp->iMeterArray = cmMemAllocZ( double, cp->status.iMeterCnt ); cp->oMeterArray = cmMemAllocZ( double, cp->status.oMeterCnt ); cp->netH = cfg->netH; + cp->serialPortH = cfg->serialPortH; // create the audio System thread if((rc = cmThreadCreate( &cp->threadH, _cmAsThreadCallback, cp, ss->args.rpt )) != kOkThRC ) @@ -874,6 +884,16 @@ cmAsRC_t cmAudioSysInitialize( cmAudioSysH_t h, const cmAudioSysCfg_t* cfg ) goto errLabel; } + // install the serial port + if( cmSeIsOpen(cp->serialPortH) ) + { + if( cmSeSetCallback(cp->serialPortH, _cmAudioSysSerialPortCallback, cp ) != kOkSeRC ) + { + rc = _cmAsError(p,kSerialPortFailAsRC,"Serial port callback installation failed."); + goto errLabel; + } + } + // setup the sub-system status notification cp->statusUpdateSmpCnt = floor(cmApBufMeterMs() * cp->ss.args.srate / 1000.0 ); cp->statusUpdateSmpIdx = 0; @@ -886,6 +906,16 @@ cmAsRC_t cmAudioSysInitialize( cmAudioSysH_t h, const cmAudioSysCfg_t* cfg ) rc = _cmAsError(p,kThreadErrAsRC,"Thread start failed."); goto errLabel; } + + if( cmSeIsOpen(cp->serialPortH) ) + { + if( cmSeStart( cp->serialPortH ) != kOkSeRC ) + { + rc = _cmAsError(p,kSerialPortFailAsRC,"Serial port start failed."); + goto errLabel; + } + + } } diff --git a/cmAudioSys.h b/cmAudioSys.h index a6b3246..4c7c5c3 100644 --- a/cmAudioSys.h +++ b/cmAudioSys.h @@ -73,6 +73,7 @@ extern "C" { kBufTooSmallAsRC, kNoMsgWaitingAsRC, kMidiSysFailAsRC, + kSerialPortFailAsRC, kMsgSerializeFailAsRC, kStateBufFailAsRC, kInvalidArgAsRC, @@ -197,6 +198,7 @@ extern "C" { cmTsQueueCb_t clientCbFunc; // Called by cmAudioSysReceiveMsg() to deliver internally generated msg's to the host. // Set to NULL if msg's will be directly returned by buffers passed to cmAudioSysReceiveMsg(). cmUdpNetH_t netH; + cmSeH_t serialPortH; } cmAudioSysCfg_t; extern cmAudioSysH_t cmAudioSysNullHandle; diff --git a/cmGlobal.h b/cmGlobal.h index 7acbd42..41f6c4d 100644 --- a/cmGlobal.h +++ b/cmGlobal.h @@ -91,6 +91,12 @@ extern "C" { //< //< If 'b' == 0 then return 'f' with the bits in 'm' cleared. //< otherwise return 'f' with the bits in 'm' set. + +// In-place assignment version of the above bit operations +#define cmSetBits(f,m) ((f) |= (m)) // Set 'f' with the bits in 'm' set. +#define cmClrBits(f,m) ((f) &= (~(m))) // Set 'f' with the bits in 'm' cleared. +#define cmTogBits(f,m) ((f)^=(m)) // Return 'f' with the bits in 'm' toggled. +#define cmEnaBits(f,m,b) ((b) ? cmSetBits(f,m) : cmClrBits(f,m)) // Set or clear bits in 'f' based on bits in 'm' and the state of 'b'. #define cmMin(v0,v1) ((v0)<(v1) ? (v0) : (v1)) //< Return the minimum arg. diff --git a/cmSerialPort.c b/cmSerialPort.c new file mode 100644 index 0000000..8f5093d --- /dev/null +++ b/cmSerialPort.c @@ -0,0 +1,579 @@ +#include "cmPrefix.h" +#include "cmGlobal.h" +#include "cmRpt.h" +#include "cmErr.h" +#include "cmCtx.h" +#include "cmMem.h" +#include "cmMallocDebug.h" +#include "cmSerialPort.h" +#include "cmThread.h" + +#include +#include +#include // close() +#include // O_RDWR +#include // TIOCEXCL + +typedef struct cmSerialPort_str +{ + cmErr_t _err; + cmThreadH_t _thH; + const char* _deviceStr; + int _deviceH; + unsigned _baudRate; + unsigned _cfgFlags; + cmSeCallbackFunc_t _cbFunc; + void* _cbArg; + struct termios _ttyAttrs; + struct pollfd _pollfd; + unsigned _pollPeriodMs; +} cmSerialPort_t; + +cmSerialPort_t* _cmSePtrFromHandle( cmSeH_t h ) +{ + cmSerialPort_t* p = (cmSerialPort_t*)h.h; + assert(p!=NULL); + return p; +} + +void _cmSeSetClosedState( cmSerialPort_t* p ) +{ + if( p->_deviceStr != NULL ) + cmMemFree((char*)(p->_deviceStr)); + + p->_deviceH = -1; + p->_deviceStr = NULL; + p->_baudRate = 0; + p->_cfgFlags = 0; + p->_cbFunc = NULL; + p->_cbArg = NULL; + +} + +cmSeRC_t _cmSeGetAttributes( cmSerialPort_t* p, struct termios* attr ) +{ + if( tcgetattr(p->_deviceH, attr) == -1 ) + return cmErrSysMsg(&p->_err,kGetAttrFailSeRC,errno,"Error getting tty attributes from %s.",p->_deviceStr); + + return kOkSeRC; +} + +cmSeRC_t _cmSePoll( cmSerialPort_t* p, unsigned timeOutMs ) +{ + cmSeRC_t rc = kOkSeRC; + int sysRC; + + if((sysRC = poll(&p->_pollfd,1,timeOutMs)) == 0) + rc = kTimeOutSeRC; + else + { + if( sysRC < 0 ) + rc = cmErrSysMsg(&p->_err,kReadFailSeRC,errno,"Poll failed on serial port."); + } + + return rc; + +} + +bool _cmSeThreadFunc(void* param) +{ + cmSerialPort_t* p = (cmSerialPort_t*)param; + cmSeH_t h; + h.h = p; + unsigned readN; + if( cmSeIsOpen(h) ) + cmSeReceiveCbTimeOut(h,p->_pollPeriodMs,&readN); + + + + return true; +} + + +cmSeRC_t _cmSeDestroy( cmSerialPort_t* p ) +{ + cmSeRC_t rc = kOkSeRC; + + + // stop the thread first + if( cmThreadDestroy(&p->_thH) != kOkThRC ) + { + rc = cmErrMsg(&p->_err,kThreadErrSeRC,"Thread destroy failed."); + goto errLabel; + } + + // Block until all written output has been sent from the device. + // Note that this call is simply passed on to the serial device driver. + // See tcsendbreak(3) ("man 3 tcsendbreak") for details. + if (tcdrain(p->_deviceH) == -1) + { + rc = cmErrSysMsg(&p->_err,kFlushFailSeRC,errno,"Error waiting for serial device '%s' to drain.", p->_deviceStr ); + goto errLabel; + } + + // It is good practice to reset a serial port back to the state in + // which you found it. This is why we saved the original termios struct + // The constant TCSANOW (defined in termios.h) indicates that + // the change should take effect immediately. + + if (tcsetattr(p->_deviceH, TCSANOW, &p->_ttyAttrs) == -1) + { + rc = cmErrSysMsg(&p->_err,kSetAttrFailSeRC,errno,"Error resetting tty attributes on serial device '%s'.",p->_deviceStr); + goto errLabel; + } + + if( p->_deviceH != -1 ) + { + if( close(p->_deviceH ) != 0 ) + { + rc = cmErrSysMsg(&p->_err,kCloseFailSeRC,errno,"Port close failed on serial dvice '%s'.", p->_deviceStr); + goto errLabel; + } + + _cmSeSetClosedState(p); + } + + cmMemPtrFree(&p); + + errLabel: + return rc; +} + + +cmSeH_t cmSeCreate( cmCtx_t* ctx, cmSeH_t* hp, const char* deviceStr, unsigned baudRate, unsigned cfgFlags, cmSeCallbackFunc_t cbFunc, void* cbArg, unsigned pollPeriodMs ) +{ + cmSeRC_t rc = kOkSeRC; + struct termios options; + cmSeH_t h; + + // if the port is already open then close it + if((rc = cmSeDestroy(hp)) != kOkSeRC ) + return *hp; + + cmSerialPort_t* p = cmMemAllocZ(cmSerialPort_t,1); + + cmErrSetup(&p->_err,&ctx->rpt,"Serial Port"); + + p->_deviceH = -1; + + // open the port + if( (p->_deviceH = open(deviceStr, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1 ) + { + rc = cmErrSysMsg(&p->_err,kOpenFailSeRC,errno,"Error opening serial '%s'",cmStringNullGuard(deviceStr)); + goto errLabel;; + } + + // Note that open() follows POSIX semantics: multiple open() calls to + // the same file will succeed unless the TIOCEXCL ioctl is issued. + // This will prevent additional opens except by root-owned processes. + // See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details. + + if( ioctl(p->_deviceH, TIOCEXCL) == -1 ) + { + rc = cmErrSysMsg(&p->_err,kResourceNotAvailableSeRC,errno,"The serial device '%s' is already in use.", cmStringNullGuard(deviceStr)); + goto errLabel; + } + + + // Now that the device is open, clear the O_NONBLOCK flag so + // subsequent I/O will block. + // See fcntl(2) ("man 2 fcntl") for details. + /* + if (fcntl(_deviceH, F_SETFL, 0) == -1) + + { + _error("Error clearing O_NONBLOCK %s - %s(%d).", pr.devFilePath.c_str(), strerror(errno), errno); + goto errLabel; + } + */ + + // Get the current options and save them so we can restore the + // default settings later. + if (tcgetattr(p->_deviceH, &p->_ttyAttrs) == -1) + { + rc = cmErrSysMsg(&p->_err,kGetAttrFailSeRC,errno,"Error getting tty attributes from the device '%s'.",deviceStr); + goto errLabel; + } + + + // The serial port attributes such as timeouts and baud rate are set by + // modifying the termios structure and then calling tcsetattr to + // cause the changes to take effect. Note that the + // changes will not take effect without the tcsetattr() call. + // See tcsetattr(4) ("man 4 tcsetattr") for details. + options = p->_ttyAttrs; + + + // Set raw input (non-canonical) mode, with reads blocking until either + // a single character has been received or a 100ms timeout expires. + // See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios") + // for details. + cfmakeraw(&options); + options.c_cc[VMIN] = 1; + options.c_cc[VTIME] = 1; + + + // The baud rate, word length, and handshake options can be set as follows: + + + // set baud rate + cfsetspeed(&options, baudRate); + + options.c_cflag |= CREAD | CLOCAL; // ignore modem controls + + // set data word size + cmClrBits(options.c_cflag, CSIZE); // clear the word size bits + cmEnaBits(options.c_cflag, CS5, cmIsFlag(cfgFlags, kDataBits5SeFl)); + cmEnaBits(options.c_cflag, CS6, cmIsFlag(cfgFlags, kDataBits6SeFl)); + cmEnaBits(options.c_cflag, CS7, cmIsFlag(cfgFlags, kDataBits7SeFl)); + cmEnaBits(options.c_cflag, CS8, cmIsFlag(cfgFlags, kDataBits8SeFl)); + + cmClrBits(options.c_cflag, PARENB); // assume no-parity + + // if the odd or even parity flag is set + if( cmIsFlag( cfgFlags, kEvenParitySeFl) || cmIsFlag( cfgFlags, kOddParitySeFl ) ) + { + cmSetBits(options.c_cflag, PARENB); + + if( cmIsFlag(cfgFlags, kOddParitySeFl ) ) + cmSetBits( options.c_cflag, PARODD); + } + + // set two stop bits + cmEnaBits( options.c_cflag, CSTOPB, cmIsFlag(cfgFlags, k2StopBitSeFl)); + + + // set hardware flow control + //cmEnaBits(options.c_cflag, CCTS_OFLOW, cmIsFlag(cfgFlags, kCTS_OutFlowCtlFl)); + //cmEnaBits(options.c_cflag, CRTS_IFLOW, cmIsFlag(cfgFlags, kRTS_InFlowCtlFl)); + //cmEnaBits(options.c_cflag, CDTR_IFLOW, cmIsFlag(cfgFlags, kDTR_InFlowCtlFl)); + //cmEnaBits(options.c_cflag, CDSR_OFLOW, cmIsFlag(cfgFlags, kDSR_OutFlowCtlFl)); + //cmEnaBits(options.c_cflag, CCAR_OFLOW, cmIsFlag(cfgFlags, kDCD_OutFlowCtlFl)); + + cmClrBits(options.c_cflag,CRTSCTS); // turn-off hardware flow control + + // 7 bit words, enable even parity, CTS out ctl flow, RTS in ctl flow + // note: set PARODD and PARENB to enable odd parity) + //options.c_cflag |= (CS7 | PARENB | CCTS_OFLOW | CRTS_IFLOW ); + + // Cause the new options to take effect immediately. + if (tcsetattr(p->_deviceH, TCSANOW, &options) == -1) + { + + rc = cmErrSysMsg(&p->_err,kSetAttrFailSeRC,errno,"Error setting tty attributes on serial device %.", deviceStr); + goto errLabel; + } + + memset(&p->_pollfd,0,sizeof(p->_pollfd)); + p->_pollfd.fd = p->_deviceH; + p->_pollfd.events = POLLIN; + + p->_deviceStr = cmMemAllocStr( deviceStr ); + p->_baudRate = baudRate; + p->_cfgFlags = cfgFlags; + p->_cbFunc = cbFunc; + p->_cbArg = cbArg; + p->_pollPeriodMs = pollPeriodMs; + + // create the listening thread + if( cmThreadCreate( &p->_thH, _cmSeThreadFunc, p, &ctx->rpt) != kOkThRC ) + { + rc = cmErrMsg(&p->_err,kThreadErrSeRC,"Thread initialization failed."); + goto errLabel; + } + + if( hp != NULL ) + hp->h = p; + else + h.h = p; + + errLabel: + if( rc != kOkSeRC ) + { + _cmSeDestroy(p); + h.h = NULL; + } + + return hp != NULL ? *hp : h; + +} + +cmSeRC_t cmSeDestroy(cmSeH_t* hp ) +{ + cmSeRC_t rc = kOkSeRC; + + if( hp==NULL || !cmSeIsOpen(*hp) ) + return kOkSeRC; + + cmSerialPort_t* p = _cmSePtrFromHandle(*hp); + + if((rc = _cmSeDestroy(p)) != kOkSeRC ) + return rc; + + hp->h = NULL; + + return rc; +} + +cmSeRC_t cmSeSetCallback( cmSeH_t h, cmSeCallbackFunc_t cbFunc, void* cbArg ) +{ + cmSerialPort_t* p = _cmSePtrFromHandle(h); + p->_cbFunc = cbFunc; + p->_cbArg = cbArg; + return kOkSeRC; +} + +cmSeRC_t cmSeStart( cmSeH_t h ) +{ + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if( cmThreadPause(p->_thH,0) != kOkThRC ) + return cmErrMsg(&p->_err,kThreadErrSeRC,0,"Thread start failed."); + + return kOkSeRC; +} + + + +bool cmSeIsOpen( cmSeH_t h) +{ + if( h.h == NULL ) + return false; + + cmSerialPort_t* p = _cmSePtrFromHandle(h); + return p->_deviceH != -1; +} + +cmSeRC_t cmSeSend( cmSeH_t h, const void* byteA, unsigned byteN ) +{ + cmSeRC_t rc = kOkSeRC; + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if( !cmSeIsOpen(h) ) + return cmErrWarnMsg( &p->_err, kResourceNotAvailableSeRC, "An attempt was made to transmit from a closed serial port."); + + if( byteN == 0 ) + return rc; + + // implement a non blocking write - if less than all the bytes were written then iterate + unsigned i = 0; + do + { + int n = 0; + if((n = write( p->_deviceH, ((char*)byteA)+i, byteN-i )) == -1 ) + { + rc = cmErrSysMsg(&p->_err,kWriteFailSeRC,errno,"Write failed on serial port '%s'.", p->_deviceStr ); + break; + } + + i += n; + + + }while( i 0 && p->_cbFunc != NULL ) + p->_cbFunc( p->_cbArg, buf, *readN_Ref ); + + return rc; + +} + +cmSeRC_t cmSeReceiveCbTimeOut( cmSeH_t h, unsigned timeOutMs, unsigned* readN_Ref) +{ + cmSeRC_t rc; + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if((rc = _cmSePoll(p,timeOutMs)) == kOkSeRC ) + rc = cmSeReceiveCbNb(h,readN_Ref); + return rc; + +} + +cmSeRC_t cmSeReceiveNb( cmSeH_t h, void* buf, unsigned bufN, unsigned* readN_Ref) +{ + cmSeRC_t rc = kOkSeRC; + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if( readN_Ref != NULL ) + *readN_Ref = 0; + + if( !cmSeIsOpen(h) ) + return cmErrWarnMsg(&p->_err, kResourceNotAvailableSeRC, "An attempt was made to read from a closed serial port."); + + int n = 0; + + // if attempt to read the port succeeded ... + if((n =read( p->_deviceH, buf, bufN )) != -1 ) + *readN_Ref = n; + else + { + // ... or failed and it wasn't because the port was empty + if( errno != EAGAIN) + rc = cmErrSysMsg(&p->_err,kReadFailSeRC,errno,"An attempt to read the serial port '%s' failed.", p->_deviceStr ); + } + + return rc; +} + +cmSeRC_t cmSeReceive( cmSeH_t h, void* buf, unsigned bufByteN, unsigned timeOutMs, unsigned* readN_Ref ) +{ + cmSeRC_t rc = kOkSeRC; + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if((rc = _cmSePoll(p,timeOutMs)) == kOkSeRC ) + rc = cmSeReceiveNb(h,buf,bufByteN,readN_Ref); + + return rc; +} + +const char* cmSeDevice( cmSeH_t h) +{ + cmSerialPort_t* p = _cmSePtrFromHandle(h); + return p->_deviceStr; +} + +unsigned cmSeBaudRate( cmSeH_t h) +{ + cmSerialPort_t* p = _cmSePtrFromHandle(h); + return p->_baudRate; +} + +unsigned cmSeCfgFlags( cmSeH_t h) +{ + cmSerialPort_t* p = _cmSePtrFromHandle(h); + return p->_cfgFlags; +} + +unsigned cmSeReadInBaudRate( cmSeH_t h ) +{ + struct termios attr; + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if((_cmSeGetAttributes(p,&attr)) != kOkSeRC ) + return 0; + + return cfgetispeed(&attr); + +} + +unsigned cmSeReadOutBaudRate( cmSeH_t h) +{ + struct termios attr; + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if((_cmSeGetAttributes(p,&attr)) != kOkSeRC ) + return 0; + + return cfgetospeed(&attr); + +} + +unsigned cmSeReadCfgFlags( cmSeH_t h) +{ + struct termios attr; + unsigned result = 0; + cmSerialPort_t* p = _cmSePtrFromHandle(h); + + if((_cmSeGetAttributes(p,&attr)) == false ) + return 0; + + switch( attr.c_cflag & CSIZE ) + { + case CS5: + cmSetBits( result, kDataBits5SeFl); + break; + + case CS6: + cmSetBits( result, kDataBits6SeFl ); + break; + + case CS7: + cmSetBits( result, kDataBits7SeFl); + break; + + case CS8: + cmSetBits( result, kDataBits8SeFl); + break; + } + + cmEnaBits( result, k2StopBitSeFl, cmIsFlag( attr.c_cflag, CSTOPB )); + cmEnaBits( result, k1StopBitSeFl, !cmIsFlag( attr.c_cflag, CSTOPB )); + + if( cmIsFlag( attr.c_cflag, PARENB ) ) + { + cmEnaBits( result, kOddParitySeFl, cmIsFlag( attr.c_cflag, PARODD )); + cmEnaBits( result, kEvenParitySeFl, !cmIsFlag( attr.c_cflag, PARODD )); + } + + return result; + +} + + +//==================================================================================================== +// +// + + +void _cmSePortTestCb( void* arg, const void* byteA, unsigned byteN ) +{ + const char* text = (const char*)byteA; + + for(unsigned i=0; i0 ) { unsigned n = cmDsvByteCount( flags, rn, argV[i].cn ); varDataByteCnt += n; @@ -238,7 +239,7 @@ void* cmDspInstAllocate( } // assign memory to the matrix types of known size - if( cmIsFlag(flags,kMtxDsvFl) && (rn*argV[i].cn) ) + if( cmIsFlag(flags,kMtxDsvFl) && (rn*argV[i].cn)>0 ) { cmDsvSetMtx( &ip->varArray[i].value, flags, varDataPtr, rn, argV[i].cn ); unsigned n = cmDsvByteCount( flags, rn, argV[i].cn ); diff --git a/dsp/cmDspFx.c b/dsp/cmDspFx.c index ecefb27..5b7a860 100644 --- a/dsp/cmDspFx.c +++ b/dsp/cmDspFx.c @@ -22,6 +22,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspKr.c b/dsp/cmDspKr.c index 2d7ad22..b098311 100644 --- a/dsp/cmDspKr.c +++ b/dsp/cmDspKr.c @@ -18,6 +18,8 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" + //( { file_desc:"'snap' audio effects performance analysis units." kw:[snap]} #include "cmTime.h" @@ -2801,6 +2803,134 @@ cmDspClass_t* cmNanoMapClassCons( cmDspCtx_t* ctx ) return &_cmNanoMapDC; } +//------------------------------------------------------------------------------------------------------------ +//) +//( { label:cmDspPicadae file_desc:"Control a MIDI synth." kw:[sunit] } + +enum +{ + kPgmPcId, + kStatusPcId, + kD0PcId, + kD1PcId, + kThruPcId +}; + +cmDspClass_t _cmPicadaeDC; + +typedef struct +{ + cmDspInst_t inst; + +} cmDspPicadae_t; + +cmDspRC_t _cmDspPicadaeSend( cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned st, unsigned d0, unsigned d1 ) +{ + cmDspSetUInt(ctx,inst,kD1PcId,d1); + cmDspSetUInt(ctx,inst,kD0PcId,d0); + cmDspSetUInt(ctx,inst,kStatusPcId,st); + return kOkDspRC; +} + +void _cmDspPicadaePgm( cmDspCtx_t* ctx, cmDspInst_t* inst, unsigned pgm ) +{ + //cmDspPicadae_t* p = (cmDspPicadae_t*)inst; + + unsigned i; + + for(i=0; iinst, kPgmPcId, 0, 0 ); + + return &p->inst; +} + +cmDspRC_t _cmDspPicadaeReset(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt ) +{ + cmDspRC_t rc = kOkDspRC; + + cmDspApplyAllDefaults(ctx,inst); + + _cmDspPicadaePgm(ctx,inst,cmDspUInt(inst,kPgmPcId)); + + return rc; +} + +cmDspRC_t _cmDspPicadaeRecv(cmDspCtx_t* ctx, cmDspInst_t* inst, const cmDspEvt_t* evt ) +{ + //cmDspPicadae_t* p = (cmDspPicadae_t*)inst; + + switch( evt->dstVarId ) + { + case kPgmPcId: + cmDspSetEvent(ctx,inst,evt); + _cmDspPicadaePgm(ctx,inst,cmDspUInt(inst,kPgmPcId)); + break; + + case kStatusPcId: + { + unsigned status = cmDsvGetUInt(evt->valuePtr); + unsigned stat_no_ch = status & 0xf0; + if( stat_no_ch == kNoteOnMdId || stat_no_ch == kNoteOffMdId || stat_no_ch == kCtlMdId ) + { + //unsigned d0 = cmDspUInt(inst,kD0PcId); + unsigned ch = 0; //d0 % 8; + status = (status & 0xf0) + ch; + cmDspSetUInt(ctx,inst,kStatusPcId,status); + } + + } + break; + + + default: + cmDspSetEvent(ctx,inst,evt); + break; + } + + + return kOkDspRC; +} + +cmDspClass_t* cmPicadaeClassCons( cmDspCtx_t* ctx ) +{ + cmDspClassSetup(&_cmPicadaeDC,ctx,"Picadae", + NULL, + _cmDspPicadaeAlloc, + NULL, + _cmDspPicadaeReset, + NULL, + _cmDspPicadaeRecv, + NULL, + NULL, + "Picadaesynth Mapper"); + + return &_cmPicadaeDC; +} + //------------------------------------------------------------------------------------------------------------ //) //( { label:cmDspRecdPlay file_desc:"Record audio segments from a live perfromance and play them back at a later time" kw:[sunit] } diff --git a/dsp/cmDspKr.h b/dsp/cmDspKr.h index 05b3c1b..9a70cb6 100644 --- a/dsp/cmDspKr.h +++ b/dsp/cmDspKr.h @@ -17,6 +17,7 @@ extern "C" { struct cmDspClass_str* cmActiveMeasClassCons( cmDspCtx_t* ctx ); struct cmDspClass_str* cmAmSyncClassCons( cmDspCtx_t* ctx ); struct cmDspClass_str* cmNanoMapClassCons( cmDspCtx_t* ctx ); + struct cmDspClass_str* cmPicadaeClassCons( cmDspCtx_t* ctx ); struct cmDspClass_str* cmRecdPlayClassCons( cmDspCtx_t* ctx ); struct cmDspClass_str* cmGoertzelClassCons( cmDspCtx_t* ctx ); struct cmDspClass_str* cmSyncRecdClassCons( cmDspCtx_t* ctx ); diff --git a/dsp/cmDspNet.c b/dsp/cmDspNet.c index a3fc466..7844a64 100644 --- a/dsp/cmDspNet.c +++ b/dsp/cmDspNet.c @@ -16,6 +16,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspNet.h b/dsp/cmDspNet.h index 9d8e9f1..f38b7aa 100644 --- a/dsp/cmDspNet.h +++ b/dsp/cmDspNet.h @@ -72,6 +72,7 @@ extern "C" { cmDspPresetMgr_t pm; unsigned nextInstId; unsigned pgmIdx; + cmSeH_t serialPortH; cmUdpNetH_t netH; // diff --git a/dsp/cmDspPgm.c b/dsp/cmDspPgm.c index aa4a993..91823af 100644 --- a/dsp/cmDspPgm.c +++ b/dsp/cmDspPgm.c @@ -17,6 +17,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspPgmKr.c b/dsp/cmDspPgmKr.c index 68fa9bb..141a319 100644 --- a/dsp/cmDspPgmKr.c +++ b/dsp/cmDspPgmKr.c @@ -19,6 +19,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspPgmKrChain.c b/dsp/cmDspPgmKrChain.c index f16865d..e5661ba 100644 --- a/dsp/cmDspPgmKrChain.c +++ b/dsp/cmDspPgmKrChain.c @@ -18,6 +18,7 @@ #include "cmUdpPort.h" #include "cmUdpNet.h" #include "cmTime.h" +#include "cmSerialPort.h" #include "cmAudioSys.h" #include "cmProcObj.h" #include "cmDspCtx.h" diff --git a/dsp/cmDspPgmKrChain2.c b/dsp/cmDspPgmKrChain2.c index f54bc25..21aaf78 100644 --- a/dsp/cmDspPgmKrChain2.c +++ b/dsp/cmDspPgmKrChain2.c @@ -17,6 +17,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspPgmKrTimeLineLite.c b/dsp/cmDspPgmKrTimeLineLite.c index 2105a17..a9a54d1 100644 --- a/dsp/cmDspPgmKrTimeLineLite.c +++ b/dsp/cmDspPgmKrTimeLineLite.c @@ -19,6 +19,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" @@ -61,7 +62,7 @@ cmDspRC_t _cmDspSysPgm_TimeLineLite(cmDspSysH_t h, void** userPtrPtr ) cmDspInst_t* ai0 = cmDspSysAllocInst(h,"AudioIn", NULL, 1, 0); cmDspInst_t* ai1 = cmDspSysAllocInst(h,"AudioIn", NULL, 1, 1); //cmDspInst_t* mip = cmDspSysAllocInst(h,"MidiIn", NULL, 2, "MOTU - Traveler mk3", "MIDI Port"); - cmDspInst_t* mip = cmDspSysAllocInst(h,"MidiIn", NULL, 2, "Apple Inc. - IAC Driver", "Bus 1"); + //cmDspInst_t* mip = cmDspSysAllocInst(h,"MidiIn", NULL, 2, "Apple Inc. - IAC Driver", "Bus 1"); cmDspInst_t* tlp = cmDspSysAllocInst(h,"TimeLine", "tl", 2, r.tlFn, r.tlPrefixPath ); cmDspInst_t* scp = cmDspSysAllocInst(h,"Score", "sc", 1, r.scFn ); diff --git a/dsp/cmDspPgmKrTksb.c b/dsp/cmDspPgmKrTksb.c index 27a9d89..184e7a0 100644 --- a/dsp/cmDspPgmKrTksb.c +++ b/dsp/cmDspPgmKrTksb.c @@ -17,6 +17,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspPgmPP.c b/dsp/cmDspPgmPP.c index db1b651..6ce3886 100644 --- a/dsp/cmDspPgmPP.c +++ b/dsp/cmDspPgmPP.c @@ -17,6 +17,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspPgmPPMain.c b/dsp/cmDspPgmPPMain.c index 0be9642..c996ae7 100644 --- a/dsp/cmDspPgmPPMain.c +++ b/dsp/cmDspPgmPPMain.c @@ -17,6 +17,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h" #include "cmProcObj.h" diff --git a/dsp/cmDspSys.c b/dsp/cmDspSys.c index fd68959..b466964 100644 --- a/dsp/cmDspSys.c +++ b/dsp/cmDspSys.c @@ -19,6 +19,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmAudioSys.h" #include "cmProcObj.h" #include "cmDspCtx.h" @@ -165,7 +166,7 @@ cmDspRC_t _cmDspSysFinalize( cmDsp_t* p ) return rc; } -cmDspRC_t cmDspSysInitialize( cmCtx_t* ctx, cmDspSysH_t* hp, cmUdpNetH_t netH ) +cmDspRC_t cmDspSysInitialize( cmCtx_t* ctx, cmDspSysH_t* hp, cmUdpNetH_t netH, cmSeH_t serialPortH ) { unsigned i; cmDspRC_t rc = kOkDspRC; @@ -178,9 +179,10 @@ cmDspRC_t cmDspSysInitialize( cmCtx_t* ctx, cmDspSysH_t* hp, cmUdpNetH_t netH ) cmErrSetup(&p->err,&ctx->rpt,"DSP System"); //p->ctx.ctx = asCtx; - p->cmCtx = *ctx; - p->netH = netH; - p->pgmIdx = cmInvalidIdx; + p->cmCtx = *ctx; + p->netH = netH; + p->serialPortH = serialPortH; + p->pgmIdx = cmInvalidIdx; // create the DSP class linked heap if(cmLHeapIsValid( p->lhH = cmLHeapCreate(1024,ctx)) == false) @@ -1560,6 +1562,13 @@ cmSymTblH_t cmDspSysSymbolTable( cmDspSysH_t h ) return p->ctx.stH; } +cmSeH_t cmDspSysSerialPort( cmDspSysH_t h ) +{ + cmDsp_t* p = _cmDspHandleToPtr(h); + return p->serialPortH; +} + + unsigned cmDspSysRegisterStaticSymbol( cmDspSysH_t h, const cmChar_t* symLabel ) { return cmSymTblRegisterStaticSymbol( cmDspSysSymbolTable(h), symLabel ); } diff --git a/dsp/cmDspSys.h b/dsp/cmDspSys.h index 8ca79ac..28fef38 100644 --- a/dsp/cmDspSys.h +++ b/dsp/cmDspSys.h @@ -23,7 +23,7 @@ extern "C" { // Control Functions // - cmDspRC_t cmDspSysInitialize( cmCtx_t* ctx, cmDspSysH_t* hp, cmUdpNetH_t netH ); + cmDspRC_t cmDspSysInitialize( cmCtx_t* ctx, cmDspSysH_t* hp, cmUdpNetH_t netH, cmSeH_t serialPortH ); cmDspRC_t cmDspSysFinalize( cmDspSysH_t* hp ); bool cmDspSysIsValid( cmDspSysH_t h ); @@ -156,6 +156,7 @@ extern "C" { double cmDspSysSampleRate( cmDspSysH_t h ); cmJsonH_t cmDspSysPgmRsrcHandle( cmDspSysH_t h ); cmSymTblH_t cmDspSysSymbolTable( cmDspSysH_t h ); + cmSeH_t cmDspSysSerialPort( cmDspSysH_t h ); unsigned cmDspSysRegisterStaticSymbol( cmDspSysH_t h, const cmChar_t* symLabel ); unsigned cmDspSysRegisterSymbol( cmDspSysH_t h, const cmChar_t* symLabel ); diff --git a/dsp/cmDspUi.c b/dsp/cmDspUi.c index ad022d5..2aff36c 100644 --- a/dsp/cmDspUi.c +++ b/dsp/cmDspUi.c @@ -19,6 +19,7 @@ #include "cmThread.h" #include "cmUdpPort.h" #include "cmUdpNet.h" +#include "cmSerialPort.h" #include "cmTime.h" #include "cmAudioSys.h"