/* 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
}