Posted to tcl by stu at Fri Jan 26 16:00:19 GMT 2018view pretty
/* * Copyright (c) 2014 Stuart Cassoff <stwo@users.sourceforge.net> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * tjsndio.c * * Tcl / Jim interface to sndio(3). * */ /* * Notes: * * sio_open() sets FD_CLOEXEC * */ #ifdef __cplusplus extern "C" { #endif #include <stdlib.h> #include <string.h> #include <sndio.h> #define COMBIEN(Z) ((int) (sizeof(Z) / sizeof(Z[0]))) #ifdef FOR_JIM #include <jim-subcmd.h> # define EZT_OK JIM_OK # define EZT_ERROR JIM_ERR # define Ezt_IncrRefCount(O) Jim_IncrRefCount((O)) # define Ezt_DecrRefCount(O) Jim_DecrRefCount(interp,(O)) # define Ezt_SetResult(O) Jim_SetResult(interp,(O)) # define Ezt_Obj Jim_Obj # define Ezt_Interp Jim_Interp # define Ezt_NewInt(I) Ezt_NewIntObj(interp,(I)) # define Ezt_GetInt(I,P) Ezt_GetIntFromJimObj(interp,(I),(P)) # define Ezt_NewWide(I) Jim_NewWideObj(interp,(I)) # define Ezt_GetWide Jim_GetWide # define Ezt_NewString(S,L) Jim_NewStringObj(interp,(S),(L)) # define Ezt_GetString Jim_GetString # define Ezt_GetBytes Jim_GetString # define Ezt_NewList() Jim_NewListObj(interp, NULL, 0) # define Ezt_ListAppend(L,O) Jim_ListAppendElement(interp,(L),(O)) # define Ezt_NewDict() Jim_NewDictObj(interp, NULL, 0) # define Ezt_DictPut(D,KO,VO) Jim_DictAddElement(interp,(D),(KO),(VO)) # define ezt_wide jim_wide # define EZT_SUBCMD(S) int (S) (Ezt_Interp *interp, int objc, Ezt_Obj *const *objv) # define EZT_CMD(S) int (S) (Ezt_Interp *interp, int objc, Ezt_Obj *const *objv) # define ezt_subcmd_type jim_subcmd_type #else # include <tcl.h> # define EZT_OK TCL_OK # define EZT_ERROR TCL_ERROR # define Ezt_IncrRefCount(O) Tcl_IncrRefCount((O)) # define Ezt_DecrRefCount(O) Tcl_DecrRefCount((O)) # define Ezt_SetResult(O) Tcl_SetObjResult(interp,(O)) # define Ezt_Obj Tcl_Obj # define Ezt_Interp Tcl_Interp # define Ezt_NewInt Tcl_NewIntObj # define Ezt_GetInt(I,P) Tcl_GetIntFromObj(interp,(I),(P)) # define Ezt_NewWide Tcl_NewWideIntObj # define Ezt_GetWide Tcl_GetWideIntFromObj # define Ezt_NewString Tcl_NewStringObj # define Ezt_GetString Tcl_GetStringFromObj # define Ezt_GetBytes Tcl_GetByteArrayFromObj # define Ezt_NewList() Tcl_NewListObj(0,NULL) # define Ezt_ListAppend(L,O) Tcl_ListObjAppendElement(interp,(L),(O)) # define Ezt_NewDict() Tcl_NewDictObj() # define Ezt_DictPut(D,KO,VO) Tcl_DictObjPut(interp,(D),(KO),(VO)) # define ezt_wide TclWideInt # define EZT_SUBCMD(S) int (S) (ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]) # define EZT_CMD EZT_SUBCMD typedef struct { const char *name; const char *args; Tcl_ObjCmdProc *proc; short minargs; short maxargs; unsigned short flags; } ezt_subcmd_type; #endif #ifdef FOR_JIM static Jim_Obj * Ezt_NewIntObj (Jim_Interp *interp, int i) { return Jim_NewWideObj(interp, (ezt_wide) i); } static int Ezt_GetIntFromJimObj (Jim_Interp *interp, Jim_Obj *objPtr, int *intPtr) { jim_wide w; if (Jim_GetWide(interp, objPtr, &w) != JIM_OK) { return JIM_ERR; } *intPtr = (int) w; return JIM_OK; } #endif typedef struct TJsio { struct sio_hdl *hd; Ezt_Obj *name; } TJsio; static TJsio TJ; static TJsio *tj = &TJ; static EZT_SUBCMD(TJsio_Sndio_Open); static EZT_SUBCMD(TJsio_Sndio_Close); static EZT_SUBCMD(TJsio_Sndio_Cap); static EZT_SUBCMD(TJsio_Sndio_Start); static EZT_SUBCMD(TJsio_Sndio_Stop); static EZT_SUBCMD(TJsio_Sndio_Write); static EZT_SUBCMD(TJsio_Sndio_Configure); static EZT_SUBCMD(TJsio_GOpts_Cmd); static EZT_SUBCMD(TJsio_SOpts_Cmd); static const ezt_subcmd_type TJsio_Cmds[] = { { "open" , NULL , TJsio_Sndio_Open , 0, 0, 0 }, { "close" , NULL , TJsio_Sndio_Close , 0, 0, 0 }, { "cap" , NULL , TJsio_Sndio_Cap , 0, 0, 0 }, { "start" , NULL , TJsio_Sndio_Start , 0, 0, 0 }, { "stop" , NULL , TJsio_Sndio_Stop , 0, 0, 0 }, { "write" , "buf" , TJsio_Sndio_Write , 1, 1, 0 }, { "configure" , NULL , TJsio_Sndio_Configure , 0, -1, 0 }, { "gopts" , NULL , TJsio_GOpts_Cmd , 0, 0, 0 }, { "sopts" , NULL , TJsio_SOpts_Cmd , 0, 0, 0 }, { NULL , NULL , NULL , 0, 0, 0 } }; /***/ enum tjsioOptions { TJSIO_OPT_BITS = (1 << 0), TJSIO_OPT_BPS = (1 << 1), TJSIO_OPT_SIG = (1 << 2), TJSIO_OPT_LE = (1 << 3), TJSIO_OPT_MSB = (1 << 4), TJSIO_OPT_PCHAN = (1 << 5), TJSIO_OPT_RCHAN = (1 << 6), TJSIO_OPT_RATE = (1 << 7), TJSIO_OPT_APPBUFSZ = (1 << 8), TJSIO_OPT_BUFSZ = (1 << 9), TJSIO_OPT_ROUND = (1 << 10), TJSIO_OPT_XRUN = (1 << 11), TJZIO_ALL = ( 0 | TJSIO_OPT_BITS | TJSIO_OPT_BPS | TJSIO_OPT_SIG | TJSIO_OPT_LE | TJSIO_OPT_MSB | TJSIO_OPT_PCHAN | TJSIO_OPT_RCHAN | TJSIO_OPT_RATE | TJSIO_OPT_APPBUFSZ | TJSIO_OPT_BUFSZ | TJSIO_OPT_ROUND | TJSIO_OPT_XRUN ), TJSIO_OPT_INVALID = -1 }; typedef struct TJsio_OptVals { int rate; } TJsio_OptVals; enum tjsioOptionFlags { TJSIO_OPTFLAG_RD = (1 << 0), TJSIO_OPTFLAG_WR = (1 << 1), TJSIO_OPTFLAG_CR = (1 << 2), TJSIO_OPTFLAG_RW = (TJSIO_OPTFLAG_WR | TJSIO_OPTFLAG_RD), TJSIO_OPTFLAG_IV = 0 }; typedef struct TJsio_Option { const char* name; enum tjsioOptions id; enum tjsioOptionFlags flags; } TJsio_Option; static TJsio_Option TJsio_Options[] = { { "-bits" , TJSIO_OPT_BITS , TJSIO_OPTFLAG_RW }, { "-bps" , TJSIO_OPT_BPS , TJSIO_OPTFLAG_RW }, { "-sig" , TJSIO_OPT_SIG , TJSIO_OPTFLAG_RW }, { "-le" , TJSIO_OPT_LE , TJSIO_OPTFLAG_RW }, { "-msb" , TJSIO_OPT_MSB , TJSIO_OPTFLAG_RW }, { "-pchan" , TJSIO_OPT_PCHAN , TJSIO_OPTFLAG_RW }, { "-rchan" , TJSIO_OPT_RCHAN , TJSIO_OPTFLAG_RW }, { "-rate" , TJSIO_OPT_RATE , TJSIO_OPTFLAG_RW }, { "-appbufsz" , TJSIO_OPT_APPBUFSZ , TJSIO_OPTFLAG_RW }, { "-bufsz" , TJSIO_OPT_BUFSZ , TJSIO_OPTFLAG_RW }, { "-round" , TJSIO_OPT_ROUND , TJSIO_OPTFLAG_RW }, { "-xrun" , TJSIO_OPT_XRUN , TJSIO_OPTFLAG_RW }, { NULL , TJSIO_OPT_INVALID , TJSIO_OPTFLAG_IV } }; #define TJSIO_XRUN_BAD (SIO_ERROR + SIO_IGNORE + SIO_SYNC) typedef struct TJsio_Xrun { const char *name; unsigned int val; } TJsio_Xrun; static TJsio_Xrun TJsio_Xruns[] = { { "error" , SIO_ERROR }, { "ignore" , SIO_IGNORE }, { "sync" , SIO_SYNC }, { NULL , 0 } }; const char * TJsio_XrunToStr (unsigned int val) { TJsio_Xrun *x; for (x = &TJsio_Xruns[0]; x->name != NULL; x++) { if (x->val == val) { return x->name; } } return NULL; } unsigned int TJsio_StrToXrun (const char *name) { TJsio_Xrun *x; for (x = &TJsio_Xruns[0]; x->name != NULL; x++) { if (strcmp(name, x->name) == 0) { return x->val; } } return TJSIO_XRUN_BAD; } /***/ static EZT_SUBCMD(TJsio_Sndio_Open) { const char *name = SIO_DEVANY; int mode = SIO_PLAY; if (tj->hd != NULL) { Ezt_SetResult(tj->name); return EZT_OK; } tj->hd = sio_open(name, mode, 0); if (tj->hd == NULL) { /*Jim_SetResultFormatted(interp, "couldn't open \"%s\"", name);*/ return EZT_ERROR; } tj->name = Ezt_NewString(name, -1); Ezt_IncrRefCount(tj->name); Ezt_SetResult(tj->name); return EZT_OK; } static EZT_SUBCMD(TJsio_Sndio_Close) { sio_close(tj->hd); tj->hd = NULL; Ezt_DecrRefCount(tj->name); return EZT_OK; } static EZT_SUBCMD(TJsio_Sndio_Cap) { struct sio_cap cap; int i, j; Ezt_Obj *l, *d; Ezt_Obj *rates, *rchans, *pchans, *encs, *denc; unsigned int rate, rchan, pchan, enc; if (tj->hd == NULL) { return EZT_ERROR; } if (!sio_getcap(tj->hd, &cap)) { /*Jim_SetResultFormatted(interp, "couldn't cat cap from \"%s\"", "cow");*/ return EZT_ERROR; } l = Ezt_NewList(); Ezt_IncrRefCount(l); for (i = 0; i < cap.nconf; i++) { d = Ezt_NewDict(); Ezt_IncrRefCount(d); rates = Ezt_NewList(); Ezt_IncrRefCount(rates); rchans = Ezt_NewList(); Ezt_IncrRefCount(rchans); pchans = Ezt_NewList(); Ezt_IncrRefCount(pchans); encs = Ezt_NewList(); Ezt_IncrRefCount(encs); for ( rate = cap.confs[i].rate, rchan = cap.confs[i].rchan, pchan = cap.confs[i].pchan, enc = cap.confs[i].enc, j = 0 ; rate || rchan || pchan || enc ; rate >>= 1, rchan >>= 1, pchan >>= 1, enc >>= 1, j++ ) { if (rate & 1) { Ezt_ListAppend(rates, Ezt_NewInt(cap.rate[j])); } if (rchan & 1) { Ezt_ListAppend(rchans, Ezt_NewInt(cap.rchan[j])); } if (pchan & 1) { Ezt_ListAppend(pchans, Ezt_NewInt(cap.pchan[j])); } if (enc & 1) { denc = Ezt_NewDict(); Ezt_IncrRefCount(denc); Ezt_DictPut(denc, Ezt_NewString("-bits", -1), Ezt_NewInt(cap.enc[j].bits)); Ezt_DictPut(denc, Ezt_NewString("-bps", -1), Ezt_NewInt(cap.enc[j].bps)); Ezt_DictPut(denc, Ezt_NewString("-sig", -1), Ezt_NewInt(cap.enc[j].sig)); Ezt_DictPut(denc, Ezt_NewString("-le", -1), Ezt_NewInt(cap.enc[j].le)); Ezt_DictPut(denc, Ezt_NewString("-msb", -1), Ezt_NewInt(cap.enc[j].msb)); Ezt_ListAppend(encs, denc); Ezt_DecrRefCount(denc); } } Ezt_DictPut(d, Ezt_NewString("rates" , -1), rates ); Ezt_DecrRefCount(rates); Ezt_DictPut(d, Ezt_NewString("rchans", -1), rchans); Ezt_DecrRefCount(rchans); Ezt_DictPut(d, Ezt_NewString("pchans", -1), pchans); Ezt_DecrRefCount(pchans); Ezt_DictPut(d, Ezt_NewString("encs" , -1), encs ); Ezt_DecrRefCount(encs); Ezt_ListAppend(l, d); Ezt_DecrRefCount(d); } Ezt_SetResult(l); Ezt_DecrRefCount(l); return EZT_OK; } static EZT_SUBCMD(TJsio_Sndio_Start) { if (tj->hd == NULL) { return EZT_ERROR; } if (!sio_start(tj->hd)) { return EZT_ERROR; } return EZT_OK; } static EZT_SUBCMD(TJsio_Sndio_Stop) { if (tj->hd == NULL) { return EZT_ERROR; } if (!sio_stop(tj->hd)) { return EZT_ERROR; } return EZT_OK; } static EZT_SUBCMD(TJsio_Sndio_Write) { const char *buf; int len; #if FOR_TCL objc--; objv++; #endif if (tj->hd == NULL) { return EZT_ERROR; } buf = Ezt_GetBytes(objv[0], &len); sio_write(tj->hd, buf, len); return EZT_OK; } /* Returned obj will have refcount 1 */ static Ezt_Obj * TJsio_ListOptions ( Ezt_Interp *interp, enum tjsioOptionFlags mask ) { int i; Ezt_Obj *o; o = Ezt_NewList(); Ezt_IncrRefCount(o); for (i = 0; i < COMBIEN(TJsio_Options) - 1; i++) { if ((TJsio_Options[i].flags & mask) != mask) { continue; } Ezt_ListAppend(o, Ezt_NewString(TJsio_Options[i].name,-1)); } return o; } static int TJsio_DoListOptions ( Ezt_Interp *interp, enum tjsioOptionFlags mask ) { Ezt_Obj *o; o = TJsio_ListOptions(interp, mask); Ezt_SetResult(o); Ezt_DecrRefCount(o); return EZT_OK; } static EZT_SUBCMD(TJsio_GOpts_Cmd) { return TJsio_DoListOptions(interp, TJSIO_OPTFLAG_RD); } static EZT_SUBCMD(TJsio_SOpts_Cmd) { return TJsio_DoListOptions(interp, TJSIO_OPTFLAG_WR); } static Ezt_Obj * TJsio_SndioParToObj ( Ezt_Interp *interp, struct sio_par *p, enum tjsioOptions opt ) { unsigned int val; switch (opt) { case TJSIO_OPT_BITS : val = p->bits ; break; case TJSIO_OPT_BPS : val = p->bps ; break; case TJSIO_OPT_SIG : val = p->sig ; break; case TJSIO_OPT_LE : val = p->le ; break; case TJSIO_OPT_MSB : val = p->msb ; break; case TJSIO_OPT_PCHAN : val = p->pchan ; break; case TJSIO_OPT_RCHAN : val = p->rchan ; break; case TJSIO_OPT_RATE : val = p->rate ; break; case TJSIO_OPT_APPBUFSZ : val = p->appbufsz ; break; case TJSIO_OPT_BUFSZ : val = p->bufsz ; break; case TJSIO_OPT_ROUND : val = p->round ; break; case TJSIO_OPT_XRUN: return Ezt_NewString(TJsio_XrunToStr(p->xrun), -1); default: return NULL; } return Ezt_NewInt(val); } static int TJsio_ObjToSndioPar ( Ezt_Interp *interp, struct sio_par *p, enum tjsioOptions opt, Ezt_Obj *optVal ) { unsigned int val; if (opt == TJSIO_OPT_XRUN) { if ((val = TJsio_StrToXrun(Ezt_GetString(optVal, NULL))) == TJSIO_XRUN_BAD) { return EZT_ERROR; } } else { if (Ezt_GetInt(optVal, &val) != EZT_OK) { return EZT_ERROR; } } switch (opt) { case TJSIO_OPT_BITS: p->bits = val; break; case TJSIO_OPT_BPS: p->bps = val; break; case TJSIO_OPT_SIG: p->sig = val; break; case TJSIO_OPT_LE: p->le = val; break; case TJSIO_OPT_MSB: p->msb = val; break; case TJSIO_OPT_PCHAN: p->pchan = val; break; case TJSIO_OPT_RCHAN: p->rchan = val; break; case TJSIO_OPT_RATE: p->rate = val; break; case TJSIO_OPT_APPBUFSZ: p->appbufsz = val; break; case TJSIO_OPT_BUFSZ: p->bufsz = val; break; case TJSIO_OPT_ROUND: p->round = val; break; case TJSIO_OPT_XRUN: p->xrun = val; break; default: return EZT_ERROR; } return EZT_OK; } static int Ezt_FindOption ( Ezt_Interp *interp, Ezt_Obj *option, enum tjsioOptionFlags mask, int *where ) { const char *optionStr = Ezt_GetString(option, NULL); int i; for (i = 0; i < COMBIEN(TJsio_Options) - 1; i++) { if ((TJsio_Options[i].flags & mask) != mask) { continue; } if (strcmp(optionStr, TJsio_Options[i].name) != 0) { continue; } *where = i; return EZT_OK; } /* return Duft_BadOption(interp, optionStr, mask, "option");*/ return EZT_ERROR; } static EZT_SUBCMD(TJsio_Sndio_Configure) { struct sio_par par; int i; #if FOR_TCL objc--; objv++; #endif if (tj->hd == NULL) { return EZT_ERROR; } if ((objc > 1) && ((objc % 2) == 1)) { /*Ezt_WrongNumArgs(interp, 1, objv, "id ?-option value ...?");*/ return EZT_ERROR; } sio_initpar(&par); if (objc > 1) { int j,k; for (j=0,k=1; k < objc; j+=2,k+=2) { if (Ezt_FindOption(interp, objv[j], TJSIO_OPTFLAG_RD, &i) != EZT_OK) { return EZT_ERROR; } if (TJsio_ObjToSndioPar(interp, &par, TJsio_Options[i].id, objv[k]) != EZT_OK) { return EZT_ERROR; } } if (!sio_setpar(tj->hd, &par)) { return EZT_ERROR; } return EZT_OK; } if (!sio_getpar(tj->hd, &par)) { return EZT_ERROR; } if (objc == 0) { Ezt_Obj *d; d = Ezt_NewDict(); Ezt_IncrRefCount(d); for (i = 0; i < COMBIEN(TJsio_Options) - 1; i++) { Ezt_DictPut(d, Ezt_NewString(TJsio_Options[i].name, -1), TJsio_SndioParToObj(interp, &par, TJsio_Options[i].id)); } Ezt_SetResult(d); Ezt_DecrRefCount(d); return EZT_OK; } if (Ezt_FindOption(interp, objv[0], TJSIO_OPTFLAG_RD, &i) != EZT_OK) { return EZT_ERROR; } Ezt_SetResult(TJsio_SndioParToObj(interp, &par, TJsio_Options[i].id)); return EZT_OK; } /***/ #ifdef FOR_JIM int Jim_sndioInit (Ezt_Interp *interp) { if (Jim_CreateCommand(interp, "sndio", Jim_SubCmdProc, (void *) TJsio_Cmds, NULL) != EZT_OK) { return EZT_ERROR; } if (Jim_PackageProvide(interp, PACKAGE_NAME, PACKAGE_VERSION, JIM_ERRMSG) != EZT_OK) { return EZT_ERROR; } return EZT_OK; } #else #define MIN_TCL_VERSION "8.5" #define TJSIO_NS "::sndio" #define TJSIO_EN "sndio" #define TJSIO_VER "0.1" static int TJsio_CommonInit ( Tcl_Interp *interp ) { if (Tcl_InitStubs (interp, MIN_TCL_VERSION, 0) == NULL) { return TCL_ERROR; } if (Tcl_PkgRequire (interp, "Tcl", MIN_TCL_VERSION, 0) == NULL) { return TCL_ERROR; } if (Tcl_CreateNamespace (interp, TJSIO_NS, NULL, NULL) == NULL) { return TCL_ERROR; } if (Tcl_PkgProvide (interp, "sndio", TJSIO_VER) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } EXTERN int Sndio_Init ( Tcl_Interp *interp ) { Tcl_Namespace *ns; const ezt_subcmd_type *c; Tcl_Obj *o; Tcl_Obj *map; if (TJsio_CommonInit(interp) != TCL_OK) { return TCL_ERROR; } if ((ns = Tcl_FindNamespace(interp, TJSIO_NS, NULL, TCL_LEAVE_ERR_MSG)) == NULL) { return TCL_ERROR; } map = Tcl_NewDictObj(); for (c = &TJsio_Cmds[0]; c->name != NULL; c++) { o = Tcl_ObjPrintf("%s%s", TJSIO_NS, c->name); Tcl_IncrRefCount(o); if (Tcl_CreateObjCommand(interp, Tcl_GetString(o), c->proc, NULL, NULL) == NULL) { Tcl_DecrRefCount(o); return TCL_ERROR; } Tcl_DictObjPut(interp, map, Tcl_NewStringObj(c->name, -1), o); Tcl_DecrRefCount(o); if (Tcl_Export(interp, ns, c->name, 0) != TCL_OK) { return TCL_ERROR; } } if (Tcl_SetEnsembleMappingDict(interp, Tcl_CreateEnsemble(interp, TJSIO_EN, ns, TCL_ENSEMBLE_PREFIX), map) != TCL_OK) { return TCL_ERROR; }; if (Tcl_Export(interp, ns, TJSIO_EN, 0) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } EXTERN int Sndio_SafeInit ( Tcl_Interp *interp ) { return TJsio_CommonInit(interp); } #endif #ifdef __cplusplus } #endif /* EOF */