Home | History | Annotate | Download | only in Modules
      1 /* CD module -- interface to Mark Callow's and Roger Chickering's */
      2  /* CD Audio Library (CD). */
      3 
      4 #include <sys/types.h>
      5 #include <cdaudio.h>
      6 #include "Python.h"
      7 
      8 #define NCALLBACKS      8
      9 
     10 typedef struct {
     11     PyObject_HEAD
     12     CDPLAYER *ob_cdplayer;
     13 } cdplayerobject;
     14 
     15 static PyObject *CdError;               /* exception cd.error */
     16 
     17 static PyObject *
     18 CD_allowremoval(cdplayerobject *self, PyObject *args)
     19 {
     20     if (!PyArg_ParseTuple(args, ":allowremoval"))
     21         return NULL;
     22 
     23     CDallowremoval(self->ob_cdplayer);
     24 
     25     Py_INCREF(Py_None);
     26     return Py_None;
     27 }
     28 
     29 static PyObject *
     30 CD_preventremoval(cdplayerobject *self, PyObject *args)
     31 {
     32     if (!PyArg_ParseTuple(args, ":preventremoval"))
     33         return NULL;
     34 
     35     CDpreventremoval(self->ob_cdplayer);
     36 
     37     Py_INCREF(Py_None);
     38     return Py_None;
     39 }
     40 
     41 static PyObject *
     42 CD_bestreadsize(cdplayerobject *self, PyObject *args)
     43 {
     44     if (!PyArg_ParseTuple(args, ":bestreadsize"))
     45         return NULL;
     46 
     47     return PyInt_FromLong((long) CDbestreadsize(self->ob_cdplayer));
     48 }
     49 
     50 static PyObject *
     51 CD_close(cdplayerobject *self, PyObject *args)
     52 {
     53     if (!PyArg_ParseTuple(args, ":close"))
     54         return NULL;
     55 
     56     if (!CDclose(self->ob_cdplayer)) {
     57         PyErr_SetFromErrno(CdError); /* XXX - ??? */
     58         return NULL;
     59     }
     60     self->ob_cdplayer = NULL;
     61 
     62     Py_INCREF(Py_None);
     63     return Py_None;
     64 }
     65 
     66 static PyObject *
     67 CD_eject(cdplayerobject *self, PyObject *args)
     68 {
     69     CDSTATUS status;
     70 
     71     if (!PyArg_ParseTuple(args, ":eject"))
     72         return NULL;
     73 
     74     if (!CDeject(self->ob_cdplayer)) {
     75         if (CDgetstatus(self->ob_cdplayer, &status) &&
     76             status.state == CD_NODISC)
     77             PyErr_SetString(CdError, "no disc in player");
     78         else
     79             PyErr_SetString(CdError, "eject failed");
     80         return NULL;
     81     }
     82 
     83     Py_INCREF(Py_None);
     84     return Py_None;
     85 }
     86 
     87 static PyObject *
     88 CD_getstatus(cdplayerobject *self, PyObject *args)
     89 {
     90     CDSTATUS status;
     91 
     92     if (!PyArg_ParseTuple(args, ":getstatus"))
     93         return NULL;
     94 
     95     if (!CDgetstatus(self->ob_cdplayer, &status)) {
     96         PyErr_SetFromErrno(CdError); /* XXX - ??? */
     97         return NULL;
     98     }
     99 
    100     return Py_BuildValue("(ii(iii)(iii)(iii)iiii)", status.state,
    101                    status.track, status.min, status.sec, status.frame,
    102                    status.abs_min, status.abs_sec, status.abs_frame,
    103                    status.total_min, status.total_sec, status.total_frame,
    104                    status.first, status.last, status.scsi_audio,
    105                    status.cur_block);
    106 }
    107 
    108 static PyObject *
    109 CD_gettrackinfo(cdplayerobject *self, PyObject *args)
    110 {
    111     int track;
    112     CDTRACKINFO info;
    113     CDSTATUS status;
    114 
    115     if (!PyArg_ParseTuple(args, "i:gettrackinfo", &track))
    116         return NULL;
    117 
    118     if (!CDgettrackinfo(self->ob_cdplayer, track, &info)) {
    119         if (CDgetstatus(self->ob_cdplayer, &status) &&
    120             status.state == CD_NODISC)
    121             PyErr_SetString(CdError, "no disc in player");
    122         else
    123             PyErr_SetString(CdError, "gettrackinfo failed");
    124         return NULL;
    125     }
    126 
    127     return Py_BuildValue("((iii)(iii))",
    128                    info.start_min, info.start_sec, info.start_frame,
    129                    info.total_min, info.total_sec, info.total_frame);
    130 }
    131 
    132 static PyObject *
    133 CD_msftoblock(cdplayerobject *self, PyObject *args)
    134 {
    135     int min, sec, frame;
    136 
    137     if (!PyArg_ParseTuple(args, "iii:msftoblock", &min, &sec, &frame))
    138         return NULL;
    139 
    140     return PyInt_FromLong((long) CDmsftoblock(self->ob_cdplayer,
    141                                             min, sec, frame));
    142 }
    143 
    144 static PyObject *
    145 CD_play(cdplayerobject *self, PyObject *args)
    146 {
    147     int start, play;
    148     CDSTATUS status;
    149 
    150     if (!PyArg_ParseTuple(args, "ii:play", &start, &play))
    151         return NULL;
    152 
    153     if (!CDplay(self->ob_cdplayer, start, play)) {
    154         if (CDgetstatus(self->ob_cdplayer, &status) &&
    155             status.state == CD_NODISC)
    156             PyErr_SetString(CdError, "no disc in player");
    157         else
    158             PyErr_SetString(CdError, "play failed");
    159         return NULL;
    160     }
    161 
    162     Py_INCREF(Py_None);
    163     return Py_None;
    164 }
    165 
    166 static PyObject *
    167 CD_playabs(cdplayerobject *self, PyObject *args)
    168 {
    169     int min, sec, frame, play;
    170     CDSTATUS status;
    171 
    172     if (!PyArg_ParseTuple(args, "iiii:playabs", &min, &sec, &frame, &play))
    173         return NULL;
    174 
    175     if (!CDplayabs(self->ob_cdplayer, min, sec, frame, play)) {
    176         if (CDgetstatus(self->ob_cdplayer, &status) &&
    177             status.state == CD_NODISC)
    178             PyErr_SetString(CdError, "no disc in player");
    179         else
    180             PyErr_SetString(CdError, "playabs failed");
    181         return NULL;
    182     }
    183 
    184     Py_INCREF(Py_None);
    185     return Py_None;
    186 }
    187 
    188 static PyObject *
    189 CD_playtrack(cdplayerobject *self, PyObject *args)
    190 {
    191     int start, play;
    192     CDSTATUS status;
    193 
    194     if (!PyArg_ParseTuple(args, "ii:playtrack", &start, &play))
    195         return NULL;
    196 
    197     if (!CDplaytrack(self->ob_cdplayer, start, play)) {
    198         if (CDgetstatus(self->ob_cdplayer, &status) &&
    199             status.state == CD_NODISC)
    200             PyErr_SetString(CdError, "no disc in player");
    201         else
    202             PyErr_SetString(CdError, "playtrack failed");
    203         return NULL;
    204     }
    205 
    206     Py_INCREF(Py_None);
    207     return Py_None;
    208 }
    209 
    210 static PyObject *
    211 CD_playtrackabs(cdplayerobject *self, PyObject *args)
    212 {
    213     int track, min, sec, frame, play;
    214     CDSTATUS status;
    215 
    216     if (!PyArg_ParseTuple(args, "iiiii:playtrackabs", &track, &min, &sec,
    217                           &frame, &play))
    218         return NULL;
    219 
    220     if (!CDplaytrackabs(self->ob_cdplayer, track, min, sec, frame, play)) {
    221         if (CDgetstatus(self->ob_cdplayer, &status) &&
    222             status.state == CD_NODISC)
    223             PyErr_SetString(CdError, "no disc in player");
    224         else
    225             PyErr_SetString(CdError, "playtrackabs failed");
    226         return NULL;
    227     }
    228 
    229     Py_INCREF(Py_None);
    230     return Py_None;
    231 }
    232 
    233 static PyObject *
    234 CD_readda(cdplayerobject *self, PyObject *args)
    235 {
    236     int numframes, n;
    237     PyObject *result;
    238 
    239     if (!PyArg_ParseTuple(args, "i:readda", &numframes))
    240         return NULL;
    241 
    242     result = PyString_FromStringAndSize(NULL, numframes * sizeof(CDFRAME));
    243     if (result == NULL)
    244         return NULL;
    245 
    246     n = CDreadda(self->ob_cdplayer,
    247                    (CDFRAME *) PyString_AsString(result), numframes);
    248     if (n == -1) {
    249         Py_DECREF(result);
    250         PyErr_SetFromErrno(CdError);
    251         return NULL;
    252     }
    253     if (n < numframes)
    254         _PyString_Resize(&result, n * sizeof(CDFRAME));
    255 
    256     return result;
    257 }
    258 
    259 static PyObject *
    260 CD_seek(cdplayerobject *self, PyObject *args)
    261 {
    262     int min, sec, frame;
    263     long PyTryBlock;
    264 
    265     if (!PyArg_ParseTuple(args, "iii:seek", &min, &sec, &frame))
    266         return NULL;
    267 
    268     PyTryBlock = CDseek(self->ob_cdplayer, min, sec, frame);
    269     if (PyTryBlock == -1) {
    270         PyErr_SetFromErrno(CdError);
    271         return NULL;
    272     }
    273 
    274     return PyInt_FromLong(PyTryBlock);
    275 }
    276 
    277 static PyObject *
    278 CD_seektrack(cdplayerobject *self, PyObject *args)
    279 {
    280     int track;
    281     long PyTryBlock;
    282 
    283     if (!PyArg_ParseTuple(args, "i:seektrack", &track))
    284         return NULL;
    285 
    286     PyTryBlock = CDseektrack(self->ob_cdplayer, track);
    287     if (PyTryBlock == -1) {
    288         PyErr_SetFromErrno(CdError);
    289         return NULL;
    290     }
    291 
    292     return PyInt_FromLong(PyTryBlock);
    293 }
    294 
    295 static PyObject *
    296 CD_seekblock(cdplayerobject *self, PyObject *args)
    297 {
    298     unsigned long PyTryBlock;
    299 
    300     if (!PyArg_ParseTuple(args, "l:seekblock", &PyTryBlock))
    301         return NULL;
    302 
    303     PyTryBlock = CDseekblock(self->ob_cdplayer, PyTryBlock);
    304     if (PyTryBlock == (unsigned long) -1) {
    305         PyErr_SetFromErrno(CdError);
    306         return NULL;
    307     }
    308 
    309     return PyInt_FromLong(PyTryBlock);
    310 }
    311 
    312 static PyObject *
    313 CD_stop(cdplayerobject *self, PyObject *args)
    314 {
    315     CDSTATUS status;
    316 
    317     if (!PyArg_ParseTuple(args, ":stop"))
    318         return NULL;
    319 
    320     if (!CDstop(self->ob_cdplayer)) {
    321         if (CDgetstatus(self->ob_cdplayer, &status) &&
    322             status.state == CD_NODISC)
    323             PyErr_SetString(CdError, "no disc in player");
    324         else
    325             PyErr_SetString(CdError, "stop failed");
    326         return NULL;
    327     }
    328 
    329     Py_INCREF(Py_None);
    330     return Py_None;
    331 }
    332 
    333 static PyObject *
    334 CD_togglepause(cdplayerobject *self, PyObject *args)
    335 {
    336     CDSTATUS status;
    337 
    338     if (!PyArg_ParseTuple(args, ":togglepause"))
    339         return NULL;
    340 
    341     if (!CDtogglepause(self->ob_cdplayer)) {
    342         if (CDgetstatus(self->ob_cdplayer, &status) &&
    343             status.state == CD_NODISC)
    344             PyErr_SetString(CdError, "no disc in player");
    345         else
    346             PyErr_SetString(CdError, "togglepause failed");
    347         return NULL;
    348     }
    349 
    350     Py_INCREF(Py_None);
    351     return Py_None;
    352 }
    353 
    354 static PyMethodDef cdplayer_methods[] = {
    355     {"allowremoval",            (PyCFunction)CD_allowremoval,   METH_VARARGS},
    356     {"bestreadsize",            (PyCFunction)CD_bestreadsize,   METH_VARARGS},
    357     {"close",                   (PyCFunction)CD_close,          METH_VARARGS},
    358     {"eject",                   (PyCFunction)CD_eject,          METH_VARARGS},
    359     {"getstatus",               (PyCFunction)CD_getstatus,              METH_VARARGS},
    360     {"gettrackinfo",            (PyCFunction)CD_gettrackinfo,   METH_VARARGS},
    361     {"msftoblock",              (PyCFunction)CD_msftoblock,             METH_VARARGS},
    362     {"play",                    (PyCFunction)CD_play,           METH_VARARGS},
    363     {"playabs",                 (PyCFunction)CD_playabs,                METH_VARARGS},
    364     {"playtrack",               (PyCFunction)CD_playtrack,              METH_VARARGS},
    365     {"playtrackabs",            (PyCFunction)CD_playtrackabs,   METH_VARARGS},
    366     {"preventremoval",          (PyCFunction)CD_preventremoval, METH_VARARGS},
    367     {"readda",                  (PyCFunction)CD_readda,         METH_VARARGS},
    368     {"seek",                    (PyCFunction)CD_seek,           METH_VARARGS},
    369     {"seekblock",               (PyCFunction)CD_seekblock,              METH_VARARGS},
    370     {"seektrack",               (PyCFunction)CD_seektrack,              METH_VARARGS},
    371     {"stop",                    (PyCFunction)CD_stop,           METH_VARARGS},
    372     {"togglepause",             (PyCFunction)CD_togglepause,    METH_VARARGS},
    373     {NULL,                      NULL}           /* sentinel */
    374 };
    375 
    376 static void
    377 cdplayer_dealloc(cdplayerobject *self)
    378 {
    379     if (self->ob_cdplayer != NULL)
    380         CDclose(self->ob_cdplayer);
    381     PyObject_Del(self);
    382 }
    383 
    384 static PyObject *
    385 cdplayer_getattr(cdplayerobject *self, char *name)
    386 {
    387     if (self->ob_cdplayer == NULL) {
    388         PyErr_SetString(PyExc_RuntimeError, "no player active");
    389         return NULL;
    390     }
    391     return Py_FindMethod(cdplayer_methods, (PyObject *)self, name);
    392 }
    393 
    394 PyTypeObject CdPlayertype = {
    395     PyObject_HEAD_INIT(&PyType_Type)
    396     0,                          /*ob_size*/
    397     "cd.cdplayer",      /*tp_name*/
    398     sizeof(cdplayerobject),     /*tp_size*/
    399     0,                          /*tp_itemsize*/
    400     /* methods */
    401     (destructor)cdplayer_dealloc, /*tp_dealloc*/
    402     0,                          /*tp_print*/
    403     (getattrfunc)cdplayer_getattr, /*tp_getattr*/
    404     0,                          /*tp_setattr*/
    405     0,                          /*tp_compare*/
    406     0,                          /*tp_repr*/
    407 };
    408 
    409 static PyObject *
    410 newcdplayerobject(CDPLAYER *cdp)
    411 {
    412     cdplayerobject *p;
    413 
    414     p = PyObject_New(cdplayerobject, &CdPlayertype);
    415     if (p == NULL)
    416         return NULL;
    417     p->ob_cdplayer = cdp;
    418     return (PyObject *) p;
    419 }
    420 
    421 static PyObject *
    422 CD_open(PyObject *self, PyObject *args)
    423 {
    424     char *dev, *direction;
    425     CDPLAYER *cdp;
    426 
    427     /*
    428      * Variable number of args.
    429      * First defaults to "None", second defaults to "r".
    430      */
    431     dev = NULL;
    432     direction = "r";
    433     if (!PyArg_ParseTuple(args, "|zs:open", &dev, &direction))
    434         return NULL;
    435 
    436     cdp = CDopen(dev, direction);
    437     if (cdp == NULL) {
    438         PyErr_SetFromErrno(CdError);
    439         return NULL;
    440     }
    441 
    442     return newcdplayerobject(cdp);
    443 }
    444 
    445 typedef struct {
    446     PyObject_HEAD
    447     CDPARSER *ob_cdparser;
    448     struct {
    449         PyObject *ob_cdcallback;
    450         PyObject *ob_cdcallbackarg;
    451     } ob_cdcallbacks[NCALLBACKS];
    452 } cdparserobject;
    453 
    454 static void
    455 CD_callback(void *arg, CDDATATYPES type, void *data)
    456 {
    457     PyObject *result, *args, *v = NULL;
    458     char *p;
    459     int i;
    460     cdparserobject *self;
    461 
    462     self = (cdparserobject *) arg;
    463     args = PyTuple_New(3);
    464     if (args == NULL)
    465         return;
    466     Py_INCREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
    467     PyTuple_SetItem(args, 0, self->ob_cdcallbacks[type].ob_cdcallbackarg);
    468     PyTuple_SetItem(args, 1, PyInt_FromLong((long) type));
    469     switch (type) {
    470     case cd_audio:
    471         v = PyString_FromStringAndSize(data, CDDA_DATASIZE);
    472         break;
    473     case cd_pnum:
    474     case cd_index:
    475         v = PyInt_FromLong(((CDPROGNUM *) data)->value);
    476         break;
    477     case cd_ptime:
    478     case cd_atime:
    479 #define ptr ((struct cdtimecode *) data)
    480         v = Py_BuildValue("(iii)",
    481                     ptr->mhi * 10 + ptr->mlo,
    482                     ptr->shi * 10 + ptr->slo,
    483                     ptr->fhi * 10 + ptr->flo);
    484 #undef ptr
    485         break;
    486     case cd_catalog:
    487         v = PyString_FromStringAndSize(NULL, 13);
    488         p = PyString_AsString(v);
    489         for (i = 0; i < 13; i++)
    490             *p++ = ((char *) data)[i] + '0';
    491         break;
    492     case cd_ident:
    493 #define ptr ((struct cdident *) data)
    494         v = PyString_FromStringAndSize(NULL, 12);
    495         p = PyString_AsString(v);
    496         CDsbtoa(p, ptr->country, 2);
    497         p += 2;
    498         CDsbtoa(p, ptr->owner, 3);
    499         p += 3;
    500         *p++ = ptr->year[0] + '0';
    501         *p++ = ptr->year[1] + '0';
    502         *p++ = ptr->serial[0] + '0';
    503         *p++ = ptr->serial[1] + '0';
    504         *p++ = ptr->serial[2] + '0';
    505         *p++ = ptr->serial[3] + '0';
    506         *p++ = ptr->serial[4] + '0';
    507 #undef ptr
    508         break;
    509     case cd_control:
    510         v = PyInt_FromLong((long) *((unchar *) data));
    511         break;
    512     }
    513     PyTuple_SetItem(args, 2, v);
    514     if (PyErr_Occurred()) {
    515         Py_DECREF(args);
    516         return;
    517     }
    518 
    519     result = PyEval_CallObject(self->ob_cdcallbacks[type].ob_cdcallback,
    520                                args);
    521     Py_DECREF(args);
    522     Py_XDECREF(result);
    523 }
    524 
    525 static PyObject *
    526 CD_deleteparser(cdparserobject *self, PyObject *args)
    527 {
    528     int i;
    529 
    530     if (!PyArg_ParseTuple(args, ":deleteparser"))
    531         return NULL;
    532 
    533     CDdeleteparser(self->ob_cdparser);
    534     self->ob_cdparser = NULL;
    535 
    536     /* no sense in keeping the callbacks, so remove them */
    537     for (i = 0; i < NCALLBACKS; i++) {
    538         Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallback);
    539         Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallbackarg);
    540     }
    541 
    542     Py_INCREF(Py_None);
    543     return Py_None;
    544 }
    545 
    546 static PyObject *
    547 CD_parseframe(cdparserobject *self, PyObject *args)
    548 {
    549     char *cdfp;
    550     int length;
    551     CDFRAME *p;
    552 
    553     if (!PyArg_ParseTuple(args, "s#:parseframe", &cdfp, &length))
    554         return NULL;
    555 
    556     if (length % sizeof(CDFRAME) != 0) {
    557         PyErr_SetString(PyExc_TypeError, "bad length");
    558         return NULL;
    559     }
    560 
    561     p = (CDFRAME *) cdfp;
    562     while (length > 0) {
    563         CDparseframe(self->ob_cdparser, p);
    564         length -= sizeof(CDFRAME);
    565         p++;
    566         if (PyErr_Occurred())
    567             return NULL;
    568     }
    569 
    570     Py_INCREF(Py_None);
    571     return Py_None;
    572 }
    573 
    574 static PyObject *
    575 CD_removecallback(cdparserobject *self, PyObject *args)
    576 {
    577     int type;
    578 
    579     if (!PyArg_ParseTuple(args, "i:removecallback", &type))
    580         return NULL;
    581 
    582     if (type < 0 || type >= NCALLBACKS) {
    583         PyErr_SetString(PyExc_TypeError, "bad type");
    584         return NULL;
    585     }
    586 
    587     CDremovecallback(self->ob_cdparser, (CDDATATYPES) type);
    588 
    589     Py_CLEAR(self->ob_cdcallbacks[type].ob_cdcallback);
    590 
    591     Py_CLEAR(self->ob_cdcallbacks[type].ob_cdcallbackarg);
    592 
    593     Py_INCREF(Py_None);
    594     return Py_None;
    595 }
    596 
    597 static PyObject *
    598 CD_resetparser(cdparserobject *self, PyObject *args)
    599 {
    600     if (!PyArg_ParseTuple(args, ":resetparser"))
    601         return NULL;
    602 
    603     CDresetparser(self->ob_cdparser);
    604 
    605     Py_INCREF(Py_None);
    606     return Py_None;
    607 }
    608 
    609 static PyObject *
    610 CD_addcallback(cdparserobject *self, PyObject *args)
    611 {
    612     int type;
    613     PyObject *func, *funcarg;
    614 
    615     /* XXX - more work here */
    616     if (!PyArg_ParseTuple(args, "iOO:addcallback", &type, &func, &funcarg))
    617         return NULL;
    618 
    619     if (type < 0 || type >= NCALLBACKS) {
    620         PyErr_SetString(PyExc_TypeError, "argument out of range");
    621         return NULL;
    622     }
    623 
    624 #ifdef CDsetcallback
    625     CDaddcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback,
    626                   (void *) self);
    627 #else
    628     CDsetcallback(self->ob_cdparser, (CDDATATYPES) type, CD_callback,
    629                   (void *) self);
    630 #endif
    631     Py_INCREF(func);
    632     Py_XSETREF(self->ob_cdcallbacks[type].ob_cdcallback, func);
    633     Py_INCREF(funcarg);
    634     Py_XSETREF(self->ob_cdcallbacks[type].ob_cdcallbackarg, funcarg);
    635 
    636 /*
    637     if (type == cd_audio) {
    638         sigfpe_[_UNDERFL].repls = _ZERO;
    639         handle_sigfpes(_ON, _EN_UNDERFL, NULL,
    640                                 _ABORT_ON_ERROR, NULL);
    641     }
    642 */
    643 
    644     Py_INCREF(Py_None);
    645     return Py_None;
    646 }
    647 
    648 static PyMethodDef cdparser_methods[] = {
    649     {"addcallback",             (PyCFunction)CD_addcallback,    METH_VARARGS},
    650     {"deleteparser",            (PyCFunction)CD_deleteparser,   METH_VARARGS},
    651     {"parseframe",              (PyCFunction)CD_parseframe,     METH_VARARGS},
    652     {"removecallback",          (PyCFunction)CD_removecallback, METH_VARARGS},
    653     {"resetparser",             (PyCFunction)CD_resetparser,    METH_VARARGS},
    654                                             /* backward compatibility */
    655     {"setcallback",             (PyCFunction)CD_addcallback,    METH_VARARGS},
    656     {NULL,                      NULL}           /* sentinel */
    657 };
    658 
    659 static void
    660 cdparser_dealloc(cdparserobject *self)
    661 {
    662     int i;
    663 
    664     for (i = 0; i < NCALLBACKS; i++) {
    665         Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallback);
    666         Py_CLEAR(self->ob_cdcallbacks[i].ob_cdcallbackarg);
    667     }
    668     CDdeleteparser(self->ob_cdparser);
    669     PyObject_Del(self);
    670 }
    671 
    672 static PyObject *
    673 cdparser_getattr(cdparserobject *self, char *name)
    674 {
    675     if (self->ob_cdparser == NULL) {
    676         PyErr_SetString(PyExc_RuntimeError, "no parser active");
    677         return NULL;
    678     }
    679 
    680     return Py_FindMethod(cdparser_methods, (PyObject *)self, name);
    681 }
    682 
    683 PyTypeObject CdParsertype = {
    684     PyObject_HEAD_INIT(&PyType_Type)
    685     0,                          /*ob_size*/
    686     "cd.cdparser",              /*tp_name*/
    687     sizeof(cdparserobject),     /*tp_size*/
    688     0,                          /*tp_itemsize*/
    689     /* methods */
    690     (destructor)cdparser_dealloc, /*tp_dealloc*/
    691     0,                          /*tp_print*/
    692     (getattrfunc)cdparser_getattr, /*tp_getattr*/
    693     0,                          /*tp_setattr*/
    694     0,                          /*tp_compare*/
    695     0,                          /*tp_repr*/
    696 };
    697 
    698 static PyObject *
    699 newcdparserobject(CDPARSER *cdp)
    700 {
    701     cdparserobject *p;
    702     int i;
    703 
    704     p = PyObject_New(cdparserobject, &CdParsertype);
    705     if (p == NULL)
    706         return NULL;
    707     p->ob_cdparser = cdp;
    708     for (i = 0; i < NCALLBACKS; i++) {
    709         p->ob_cdcallbacks[i].ob_cdcallback = NULL;
    710         p->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
    711     }
    712     return (PyObject *) p;
    713 }
    714 
    715 static PyObject *
    716 CD_createparser(PyObject *self, PyObject *args)
    717 {
    718     CDPARSER *cdp;
    719 
    720     if (!PyArg_ParseTuple(args, ":createparser"))
    721         return NULL;
    722     cdp = CDcreateparser();
    723     if (cdp == NULL) {
    724         PyErr_SetString(CdError, "createparser failed");
    725         return NULL;
    726     }
    727 
    728     return newcdparserobject(cdp);
    729 }
    730 
    731 static PyObject *
    732 CD_msftoframe(PyObject *self, PyObject *args)
    733 {
    734     int min, sec, frame;
    735 
    736     if (!PyArg_ParseTuple(args, "iii:msftoframe", &min, &sec, &frame))
    737         return NULL;
    738 
    739     return PyInt_FromLong((long) CDmsftoframe(min, sec, frame));
    740 }
    741 
    742 static PyMethodDef CD_methods[] = {
    743     {"open",                    (PyCFunction)CD_open,           METH_VARARGS},
    744     {"createparser",            (PyCFunction)CD_createparser,   METH_VARARGS},
    745     {"msftoframe",              (PyCFunction)CD_msftoframe,     METH_VARARGS},
    746     {NULL,              NULL}   /* Sentinel */
    747 };
    748 
    749 void
    750 initcd(void)
    751 {
    752     PyObject *m, *d;
    753 
    754     if (PyErr_WarnPy3k("the cd module has been removed in "
    755                        "Python 3.0", 2) < 0)
    756         return;
    757 
    758     m = Py_InitModule("cd", CD_methods);
    759     if (m == NULL)
    760         return;
    761     d = PyModule_GetDict(m);
    762 
    763     CdError = PyErr_NewException("cd.error", NULL, NULL);
    764     PyDict_SetItemString(d, "error", CdError);
    765 
    766     /* Identifiers for the different types of callbacks from the parser */
    767     PyDict_SetItemString(d, "audio", PyInt_FromLong((long) cd_audio));
    768     PyDict_SetItemString(d, "pnum", PyInt_FromLong((long) cd_pnum));
    769     PyDict_SetItemString(d, "index", PyInt_FromLong((long) cd_index));
    770     PyDict_SetItemString(d, "ptime", PyInt_FromLong((long) cd_ptime));
    771     PyDict_SetItemString(d, "atime", PyInt_FromLong((long) cd_atime));
    772     PyDict_SetItemString(d, "catalog", PyInt_FromLong((long) cd_catalog));
    773     PyDict_SetItemString(d, "ident", PyInt_FromLong((long) cd_ident));
    774     PyDict_SetItemString(d, "control", PyInt_FromLong((long) cd_control));
    775 
    776     /* Block size information for digital audio data */
    777     PyDict_SetItemString(d, "DATASIZE",
    778                        PyInt_FromLong((long) CDDA_DATASIZE));
    779     PyDict_SetItemString(d, "BLOCKSIZE",
    780                        PyInt_FromLong((long) CDDA_BLOCKSIZE));
    781 
    782     /* Possible states for the cd player */
    783     PyDict_SetItemString(d, "ERROR", PyInt_FromLong((long) CD_ERROR));
    784     PyDict_SetItemString(d, "NODISC", PyInt_FromLong((long) CD_NODISC));
    785     PyDict_SetItemString(d, "READY", PyInt_FromLong((long) CD_READY));
    786     PyDict_SetItemString(d, "PLAYING", PyInt_FromLong((long) CD_PLAYING));
    787     PyDict_SetItemString(d, "PAUSED", PyInt_FromLong((long) CD_PAUSED));
    788     PyDict_SetItemString(d, "STILL", PyInt_FromLong((long) CD_STILL));
    789 #ifdef CD_CDROM                 /* only newer versions of the library */
    790     PyDict_SetItemString(d, "CDROM", PyInt_FromLong((long) CD_CDROM));
    791 #endif
    792 }
    793