/* 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_XDECREF(self->ob_cdcallbacks[i].ob_cdcallback);
		self->ob_cdcallbacks[i].ob_cdcallback = NULL;
		Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg);
		self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
	}

	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_XDECREF(self->ob_cdcallbacks[type].ob_cdcallback);
	self->ob_cdcallbacks[type].ob_cdcallback = NULL;

	Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
	self->ob_cdcallbacks[type].ob_cdcallbackarg = NULL;

	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_XDECREF(self->ob_cdcallbacks[type].ob_cdcallback);
	Py_INCREF(func);
	self->ob_cdcallbacks[type].ob_cdcallback = func;
	Py_XDECREF(self->ob_cdcallbacks[type].ob_cdcallbackarg);
	Py_INCREF(funcarg);
	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_XDECREF(self->ob_cdcallbacks[i].ob_cdcallback);
		self->ob_cdcallbacks[i].ob_cdcallback = NULL;
		Py_XDECREF(self->ob_cdcallbacks[i].ob_cdcallbackarg);
		self->ob_cdcallbacks[i].ob_cdcallbackarg = NULL;
	}
	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;

	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
}
