/* CD module -- interface to Mark Callow's and Roger Chickering's */ /* CD Audio Library (CD). */ #include <sys/types.h> #include <cdaudio.h> #include "Python.h" #define NCALLBACKS 8 typedef struct { PyObject_HEAD CDPLAYER *ob_cdplayer; } cdplayerobject; static PyObject *CdError; /* exception cd.error */ static PyObject * CD_allowremoval(cdplayerobject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":allowremoval")) return NULL; CDallowremoval(self->ob_cdplayer); Py_INCREF(Py_None); return Py_None; } static PyObject * CD_preventremoval(cdplayerobject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":preventremoval")) return NULL; CDpreventremoval(self->ob_cdplayer); Py_INCREF(Py_None); return Py_None; } static PyObject * CD_bestreadsize(cdplayerobject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":bestreadsize")) return NULL; return PyInt_FromLong((long) CDbestreadsize(self->ob_cdplayer)); } static PyObject * CD_close(cdplayerobject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":close")) return NULL; if (!CDclose(self->ob_cdplayer)) { PyErr_SetFromErrno(CdError); /* XXX - ??? */ return NULL; } self->ob_cdplayer = NULL; Py_INCREF(Py_None); return Py_None; } static PyObject * CD_eject(cdplayerobject *self, PyObject *args) { CDSTATUS status; if (!PyArg_ParseTuple(args, ":eject")) return NULL; if (!CDeject(self->ob_cdplayer)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "eject failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_getstatus(cdplayerobject *self, PyObject *args) { CDSTATUS status; if (!PyArg_ParseTuple(args, ":getstatus")) return NULL; if (!CDgetstatus(self->ob_cdplayer, &status)) { PyErr_SetFromErrno(CdError); /* XXX - ??? */ return NULL; } return Py_BuildValue("(ii(iii)(iii)(iii)iiii)", status.state, status.track, status.min, status.sec, status.frame, status.abs_min, status.abs_sec, status.abs_frame, status.total_min, status.total_sec, status.total_frame, status.first, status.last, status.scsi_audio, status.cur_block); } static PyObject * CD_gettrackinfo(cdplayerobject *self, PyObject *args) { int track; CDTRACKINFO info; CDSTATUS status; if (!PyArg_ParseTuple(args, "i:gettrackinfo", &track)) return NULL; if (!CDgettrackinfo(self->ob_cdplayer, track, &info)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "gettrackinfo failed"); return NULL; } return Py_BuildValue("((iii)(iii))", info.start_min, info.start_sec, info.start_frame, info.total_min, info.total_sec, info.total_frame); } static PyObject * CD_msftoblock(cdplayerobject *self, PyObject *args) { int min, sec, frame; if (!PyArg_ParseTuple(args, "iii:msftoblock", &min, &sec, &frame)) return NULL; return PyInt_FromLong((long) CDmsftoblock(self->ob_cdplayer, min, sec, frame)); } static PyObject * CD_play(cdplayerobject *self, PyObject *args) { int start, play; CDSTATUS status; if (!PyArg_ParseTuple(args, "ii:play", &start, &play)) return NULL; if (!CDplay(self->ob_cdplayer, start, play)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "play failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_playabs(cdplayerobject *self, PyObject *args) { int min, sec, frame, play; CDSTATUS status; if (!PyArg_ParseTuple(args, "iiii:playabs", &min, &sec, &frame, &play)) return NULL; if (!CDplayabs(self->ob_cdplayer, min, sec, frame, play)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "playabs failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_playtrack(cdplayerobject *self, PyObject *args) { int start, play; CDSTATUS status; if (!PyArg_ParseTuple(args, "ii:playtrack", &start, &play)) return NULL; if (!CDplaytrack(self->ob_cdplayer, start, play)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "playtrack failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_playtrackabs(cdplayerobject *self, PyObject *args) { int track, min, sec, frame, play; CDSTATUS status; if (!PyArg_ParseTuple(args, "iiiii:playtrackabs", &track, &min, &sec, &frame, &play)) return NULL; if (!CDplaytrackabs(self->ob_cdplayer, track, min, sec, frame, play)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "playtrackabs failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_readda(cdplayerobject *self, PyObject *args) { int numframes, n; PyObject *result; if (!PyArg_ParseTuple(args, "i:readda", &numframes)) return NULL; result = PyString_FromStringAndSize(NULL, numframes * sizeof(CDFRAME)); if (result == NULL) return NULL; n = CDreadda(self->ob_cdplayer, (CDFRAME *) PyString_AsString(result), numframes); if (n == -1) { Py_DECREF(result); PyErr_SetFromErrno(CdError); return NULL; } if (n < numframes) _PyString_Resize(&result, n * sizeof(CDFRAME)); return result; } static PyObject * CD_seek(cdplayerobject *self, PyObject *args) { int min, sec, frame; long PyTryBlock; if (!PyArg_ParseTuple(args, "iii:seek", &min, &sec, &frame)) return NULL; PyTryBlock = CDseek(self->ob_cdplayer, min, sec, frame); if (PyTryBlock == -1) { PyErr_SetFromErrno(CdError); return NULL; } return PyInt_FromLong(PyTryBlock); } static PyObject * CD_seektrack(cdplayerobject *self, PyObject *args) { int track; long PyTryBlock; if (!PyArg_ParseTuple(args, "i:seektrack", &track)) return NULL; PyTryBlock = CDseektrack(self->ob_cdplayer, track); if (PyTryBlock == -1) { PyErr_SetFromErrno(CdError); return NULL; } return PyInt_FromLong(PyTryBlock); } static PyObject * CD_seekblock(cdplayerobject *self, PyObject *args) { unsigned long PyTryBlock; if (!PyArg_ParseTuple(args, "l:seekblock", &PyTryBlock)) return NULL; PyTryBlock = CDseekblock(self->ob_cdplayer, PyTryBlock); if (PyTryBlock == (unsigned long) -1) { PyErr_SetFromErrno(CdError); return NULL; } return PyInt_FromLong(PyTryBlock); } static PyObject * CD_stop(cdplayerobject *self, PyObject *args) { CDSTATUS status; if (!PyArg_ParseTuple(args, ":stop")) return NULL; if (!CDstop(self->ob_cdplayer)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "stop failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_togglepause(cdplayerobject *self, PyObject *args) { CDSTATUS status; if (!PyArg_ParseTuple(args, ":togglepause")) return NULL; if (!CDtogglepause(self->ob_cdplayer)) { if (CDgetstatus(self->ob_cdplayer, &status) && status.state == CD_NODISC) PyErr_SetString(CdError, "no disc in player"); else PyErr_SetString(CdError, "togglepause failed"); return NULL; } Py_INCREF(Py_None); return Py_None; } static PyMethodDef cdplayer_methods[] = { {"allowremoval", (PyCFunction)CD_allowremoval, METH_VARARGS}, {"bestreadsize", (PyCFunction)CD_bestreadsize, METH_VARARGS}, {"close", (PyCFunction)CD_close, METH_VARARGS}, {"eject", (PyCFunction)CD_eject, METH_VARARGS}, {"getstatus", (PyCFunction)CD_getstatus, METH_VARARGS}, {"gettrackinfo", (PyCFunction)CD_gettrackinfo, METH_VARARGS}, {"msftoblock", (PyCFunction)CD_msftoblock, METH_VARARGS}, {"play", (PyCFunction)CD_play, METH_VARARGS}, {"playabs", (PyCFunction)CD_playabs, METH_VARARGS}, {"playtrack", (PyCFunction)CD_playtrack, METH_VARARGS}, {"playtrackabs", (PyCFunction)CD_playtrackabs, METH_VARARGS}, {"preventremoval", (PyCFunction)CD_preventremoval, METH_VARARGS}, {"readda", (PyCFunction)CD_readda, METH_VARARGS}, {"seek", (PyCFunction)CD_seek, METH_VARARGS}, {"seekblock", (PyCFunction)CD_seekblock, METH_VARARGS}, {"seektrack", (PyCFunction)CD_seektrack, METH_VARARGS}, {"stop", (PyCFunction)CD_stop, METH_VARARGS}, {"togglepause", (PyCFunction)CD_togglepause, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; static void cdplayer_dealloc(cdplayerobject *self) { if (self->ob_cdplayer != NULL) CDclose(self->ob_cdplayer); PyObject_Del(self); } static PyObject * cdplayer_getattr(cdplayerobject *self, char *name) { if (self->ob_cdplayer == NULL) { PyErr_SetString(PyExc_RuntimeError, "no player active"); return NULL; } return Py_FindMethod(cdplayer_methods, (PyObject *)self, name); } PyTypeObject CdPlayertype = { PyObject_HEAD_INIT(&PyType_Type) 0, /*ob_size*/ "cd.cdplayer", /*tp_name*/ sizeof(cdplayerobject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)cdplayer_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)cdplayer_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ }; static PyObject * newcdplayerobject(CDPLAYER *cdp) { cdplayerobject *p; p = PyObject_New(cdplayerobject, &CdPlayertype); if (p == NULL) return NULL; p->ob_cdplayer = cdp; return (PyObject *) p; } static PyObject * CD_open(PyObject *self, PyObject *args) { char *dev, *direction; CDPLAYER *cdp; /* * Variable number of args. * First defaults to "None", second defaults to "r". */ dev = NULL; direction = "r"; if (!PyArg_ParseTuple(args, "|zs:open", &dev, &direction)) return NULL; cdp = CDopen(dev, direction); if (cdp == NULL) { PyErr_SetFromErrno(CdError); return NULL; } return newcdplayerobject(cdp); } typedef struct { PyObject_HEAD CDPARSER *ob_cdparser; struct { PyObject *ob_cdcallback; PyObject *ob_cdcallbackarg; } ob_cdcallbacks[NCALLBACKS]; } cdparserobject; static void CD_callback(void *arg, CDDATATYPES type, void *data) { PyObject *result, *args, *v = NULL; char *p; int i; cdparserobject *self; self = (cdparserobject *) arg; args = PyTuple_New(3); if (args == NULL) return; Py_INCREF(self->ob_cdcallbacks[type].ob_cdcallbackarg); PyTuple_SetItem(args, 0, self->ob_cdcallbacks[type].ob_cdcallbackarg); PyTuple_SetItem(args, 1, PyInt_FromLong((long) type)); switch (type) { case cd_audio: v = PyString_FromStringAndSize(data, CDDA_DATASIZE); break; case cd_pnum: case cd_index: v = PyInt_FromLong(((CDPROGNUM *) data)->value); break; case cd_ptime: case cd_atime: #define ptr ((struct cdtimecode *) data) v = Py_BuildValue("(iii)", ptr->mhi * 10 + ptr->mlo, ptr->shi * 10 + ptr->slo, ptr->fhi * 10 + ptr->flo); #undef ptr break; case cd_catalog: v = PyString_FromStringAndSize(NULL, 13); p = PyString_AsString(v); for (i = 0; i < 13; i++) *p++ = ((char *) data)[i] + '0'; break; case cd_ident: #define ptr ((struct cdident *) data) v = PyString_FromStringAndSize(NULL, 12); p = PyString_AsString(v); CDsbtoa(p, ptr->country, 2); p += 2; CDsbtoa(p, ptr->owner, 3); p += 3; *p++ = ptr->year[0] + '0'; *p++ = ptr->year[1] + '0'; *p++ = ptr->serial[0] + '0'; *p++ = ptr->serial[1] + '0'; *p++ = ptr->serial[2] + '0'; *p++ = ptr->serial[3] + '0'; *p++ = ptr->serial[4] + '0'; #undef ptr break; case cd_control: v = PyInt_FromLong((long) *((unchar *) data)); break; } PyTuple_SetItem(args, 2, v); if (PyErr_Occurred()) { Py_DECREF(args); return; } result = PyEval_CallObject(self->ob_cdcallbacks[type].ob_cdcallback, args); Py_DECREF(args); Py_XDECREF(result); } static PyObject * CD_deleteparser(cdparserobject *self, PyObject *args) { int i; if (!PyArg_ParseTuple(args, ":deleteparser")) return NULL; CDdeleteparser(self->ob_cdparser); self->ob_cdparser = NULL; /* no sense in keeping the callbacks, so remove them */ for (i = 0; i < NCALLBACKS; i++) { Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallback); Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallbackarg); } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_parseframe(cdparserobject *self, PyObject *args) { char *cdfp; int length; CDFRAME *p; if (!PyArg_ParseTuple(args, "s#:parseframe", &cdfp, &length)) return NULL; if (length % sizeof(CDFRAME) != 0) { PyErr_SetString(PyExc_TypeError, "bad length"); return NULL; } p = (CDFRAME *) cdfp; while (length > 0) { CDparseframe(self->ob_cdparser, p); length -= sizeof(CDFRAME); p++; if (PyErr_Occurred()) return NULL; } Py_INCREF(Py_None); return Py_None; } static PyObject * CD_removecallback(cdparserobject *self, PyObject *args) { int type; if (!PyArg_ParseTuple(args, "i:removecallback", &type)) return NULL; if (type < 0 || type >= NCALLBACKS) { PyErr_SetString(PyExc_TypeError, "bad type"); return NULL; } CDremovecallback(self->ob_cdparser, (CDDATATYPES) type); Py_CLEAR(self->ob_cdcallbacks[type].ob_cdcallback); Py_CLEAR(self->ob_cdcallbacks[type].ob_cdcallbackarg); Py_INCREF(Py_None); return Py_None; } static PyObject * CD_resetparser(cdparserobject *self, PyObject *args) { if (!PyArg_ParseTuple(args, ":resetparser")) return NULL; CDresetparser(self->ob_cdparser); Py_INCREF(Py_None); return Py_None; } static PyObject * CD_addcallback(cdparserobject *self, PyObject *args) { int type; PyObject *func, *funcarg; /* XXX - more work here */ if (!PyArg_ParseTuple(args, "iOO:addcallback", &type, &func, &funcarg)) return NULL; if (type < 0 || type >= NCALLBACKS) { PyErr_SetString(PyExc_TypeError, "argument out of range"); return NULL; } #ifdef CDsetcallback CDaddcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback, (void *) self); #else CDsetcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback, (void *) self); #endif Py_INCREF(func); Py_XSETREF(self->ob_cdcallbacks[type].ob_cdcallback, func); Py_INCREF(funcarg); Py_XSETREF(self->ob_cdcallbacks[type].ob_cdcallbackarg, funcarg); /* if (type == cd_audio) { sigfpe_[_UNDERFL].repls = _ZERO; handle_sigfpes(_ON, _EN_UNDERFL, NULL, _ABORT_ON_ERROR, NULL); } */ Py_INCREF(Py_None); return Py_None; } static PyMethodDef cdparser_methods[] = { {"addcallback", (PyCFunction)CD_addcallback, METH_VARARGS}, {"deleteparser", (PyCFunction)CD_deleteparser, METH_VARARGS}, {"parseframe", (PyCFunction)CD_parseframe, METH_VARARGS}, {"removecallback", (PyCFunction)CD_removecallback, METH_VARARGS}, {"resetparser", (PyCFunction)CD_resetparser, METH_VARARGS}, /* backward compatibility */ {"setcallback", (PyCFunction)CD_addcallback, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; static void cdparser_dealloc(cdparserobject *self) { int i; for (i = 0; i < NCALLBACKS; i++) { Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallback); Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallbackarg); } CDdeleteparser(self->ob_cdparser); PyObject_Del(self); } static PyObject * cdparser_getattr(cdparserobject *self, char *name) { if (self->ob_cdparser == NULL) { PyErr_SetString(PyExc_RuntimeError, "no parser active"); return NULL; } return Py_FindMethod(cdparser_methods, (PyObject *)self, name); } PyTypeObject CdParsertype = { PyObject_HEAD_INIT(&PyType_Type) 0, /*ob_size*/ "cd.cdparser", /*tp_name*/ sizeof(cdparserobject), /*tp_size*/ 0, /*tp_itemsize*/ /* methods */ (destructor)cdparser_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ (getattrfunc)cdparser_getattr, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ }; static PyObject * newcdparserobject(CDPARSER *cdp) { cdparserobject *p; int i; p = PyObject_New(cdparserobject, &CdParsertype); if (p == NULL) return NULL; p->ob_cdparser = cdp; for (i = 0; i < NCALLBACKS; i++) { p->ob_cdcallbacks[i].ob_cdcallback = NULL; p->ob_cdcallbacks[i].ob_cdcallbackarg = NULL; } return (PyObject *) p; } static PyObject * CD_createparser(PyObject *self, PyObject *args) { CDPARSER *cdp; if (!PyArg_ParseTuple(args, ":createparser")) return NULL; cdp = CDcreateparser(); if (cdp == NULL) { PyErr_SetString(CdError, "createparser failed"); return NULL; } return newcdparserobject(cdp); } static PyObject * CD_msftoframe(PyObject *self, PyObject *args) { int min, sec, frame; if (!PyArg_ParseTuple(args, "iii:msftoframe", &min, &sec, &frame)) return NULL; return PyInt_FromLong((long) CDmsftoframe(min, sec, frame)); } static PyMethodDef CD_methods[] = { {"open", (PyCFunction)CD_open, METH_VARARGS}, {"createparser", (PyCFunction)CD_createparser, METH_VARARGS}, {"msftoframe", (PyCFunction)CD_msftoframe, METH_VARARGS}, {NULL, NULL} /* Sentinel */ }; void initcd(void) { PyObject *m, *d; if (PyErr_WarnPy3k("the cd module has been removed in " "Python 3.0", 2) < 0) return; m = Py_InitModule("cd", CD_methods); if (m == NULL) return; d = PyModule_GetDict(m); CdError = PyErr_NewException("cd.error", NULL, NULL); PyDict_SetItemString(d, "error", CdError); /* Identifiers for the different types of callbacks from the parser */ PyDict_SetItemString(d, "audio", PyInt_FromLong((long) cd_audio)); PyDict_SetItemString(d, "pnum", PyInt_FromLong((long) cd_pnum)); PyDict_SetItemString(d, "index", PyInt_FromLong((long) cd_index)); PyDict_SetItemString(d, "ptime", PyInt_FromLong((long) cd_ptime)); PyDict_SetItemString(d, "atime", PyInt_FromLong((long) cd_atime)); PyDict_SetItemString(d, "catalog", PyInt_FromLong((long) cd_catalog)); PyDict_SetItemString(d, "ident", PyInt_FromLong((long) cd_ident)); PyDict_SetItemString(d, "control", PyInt_FromLong((long) cd_control)); /* Block size information for digital audio data */ PyDict_SetItemString(d, "DATASIZE", PyInt_FromLong((long) CDDA_DATASIZE)); PyDict_SetItemString(d, "BLOCKSIZE", PyInt_FromLong((long) CDDA_BLOCKSIZE)); /* Possible states for the cd player */ PyDict_SetItemString(d, "ERROR", PyInt_FromLong((long) CD_ERROR)); PyDict_SetItemString(d, "NODISC", PyInt_FromLong((long) CD_NODISC)); PyDict_SetItemString(d, "READY", PyInt_FromLong((long) CD_READY)); PyDict_SetItemString(d, "PLAYING", PyInt_FromLong((long) CD_PLAYING)); PyDict_SetItemString(d, "PAUSED", PyInt_FromLong((long) CD_PAUSED)); PyDict_SetItemString(d, "STILL", PyInt_FromLong((long) CD_STILL)); #ifdef CD_CDROM /* only newer versions of the library */ PyDict_SetItemString(d, "CDROM", PyInt_FromLong((long) CD_CDROM)); #endif }