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
10typedef struct {
11    PyObject_HEAD
12    CDPLAYER *ob_cdplayer;
13} cdplayerobject;
14
15static PyObject *CdError;               /* exception cd.error */
16
17static PyObject *
18CD_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
29static PyObject *
30CD_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
41static PyObject *
42CD_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
50static PyObject *
51CD_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
66static PyObject *
67CD_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
87static PyObject *
88CD_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
108static PyObject *
109CD_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
132static PyObject *
133CD_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
144static PyObject *
145CD_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
166static PyObject *
167CD_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
188static PyObject *
189CD_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
210static PyObject *
211CD_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
233static PyObject *
234CD_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
259static PyObject *
260CD_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
277static PyObject *
278CD_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
295static PyObject *
296CD_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
312static PyObject *
313CD_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
333static PyObject *
334CD_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
354static 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
376static void
377cdplayer_dealloc(cdplayerobject *self)
378{
379    if (self->ob_cdplayer != NULL)
380        CDclose(self->ob_cdplayer);
381    PyObject_Del(self);
382}
383
384static PyObject *
385cdplayer_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
394PyTypeObject 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
409static PyObject *
410newcdplayerobject(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
421static PyObject *
422CD_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
445typedef 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
454static void
455CD_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
525static PyObject *
526CD_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
546static PyObject *
547CD_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
574static PyObject *
575CD_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
597static PyObject *
598CD_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
609static PyObject *
610CD_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
648static 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
659static void
660cdparser_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
672static PyObject *
673cdparser_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
683PyTypeObject 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
698static PyObject *
699newcdparserobject(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
715static PyObject *
716CD_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
731static PyObject *
732CD_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
742static 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
749void
750initcd(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