/*
    pygame - Python Game Library
    Copyright (C) 2000-2001  Pete Shinners

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    Pete Shinners
    pete@shinners.org
*/

/*
 *  mixer module for pygame
 */
#define PYGAMEAPI_MIXER_INTERNAL
#include "pygame.h"
#include "mixer.h"

#define MIX_DEFAULT_CHUNKSIZE	1024



staticforward PyTypeObject PySound_Type;
staticforward PyTypeObject PyChannel_Type;
static PyObject* PySound_New(Mix_Chunk*);
static PyObject* PyChannel_New(int);
#define PySound_Check(x) ((x)->ob_type == &PySound_Type)
#define PyChannel_Check(x) ((x)->ob_type == &PyChannel_Type)


static int request_frequency = MIX_DEFAULT_FREQUENCY;
static int request_size = MIX_DEFAULT_FORMAT;
static int request_stereo = MIX_DEFAULT_CHANNELS;
static int request_chunksize = MIX_DEFAULT_CHUNKSIZE;


static int sound_init(PyObject* self, PyObject* arg, PyObject* kwarg);


struct ChannelData
{
    PyObject* sound;
    PyObject* queue;
    int endevent;
};
static struct ChannelData *channeldata = NULL;
static int numchanneldata = 0;

Mix_Music** current_music;
Mix_Music** queue_music;


static void endsound_callback(int channel)
{
    if(channeldata)
    {
	if(channeldata[channel].endevent && SDL_WasInit(SDL_INIT_VIDEO))
	{
	    SDL_Event e;
	    memset(&e, 0, sizeof(e));
	    e.type = channeldata[channel].endevent;
	    SDL_PushEvent(&e);
	}
	if(channeldata[channel].queue)
	{
	    int channelnum;
	    Mix_Chunk* sound = PySound_AsChunk(channeldata[channel].queue);
	    Py_XDECREF(channeldata[channel].sound);
	    channeldata[channel].sound = channeldata[channel].queue;
	    channeldata[channel].queue = NULL;
	    channelnum = Mix_PlayChannelTimed(channel, sound, 0, -1);
	    if(channelnum != -1)
	    	Mix_GroupChannel(channelnum, (int)sound);
	}
	else
	{
	    Py_XDECREF(channeldata[channel].sound);
	    channeldata[channel].sound = NULL;
	}
    }
}


static void autoquit(void)
{
        int i;
	if(SDL_WasInit(SDL_INIT_AUDIO))
	{
		Mix_HaltMusic();

                if(channeldata)
                {
                    for(i=0; i<numchanneldata; ++i)
		    {
                        Py_XDECREF(channeldata[i].sound);
			Py_XDECREF(channeldata[i].queue);
		    }
                    free(channeldata);
                    channeldata = NULL;
                    numchanneldata = 0;
                }

		if(current_music)
		{
			if(*current_music)
			{
				Mix_FreeMusic(*current_music);
				*current_music = NULL;
			}
			current_music = NULL;
		}
		if(queue_music)
		{
			if(*queue_music)
			{
				Mix_FreeMusic(*queue_music);
				*queue_music = NULL;
			}
			queue_music = NULL;
		}

		Mix_CloseAudio();
		SDL_QuitSubSystem(SDL_INIT_AUDIO);
	}
}


static PyObject* autoinit(PyObject* self, PyObject* arg)
{
	int freq, size, stereo, chunk;
	int i;
	freq = request_frequency;
	size = request_size;
	stereo = request_stereo;
	chunk = request_chunksize;

	if(!PyArg_ParseTuple(arg, "|iiii", &freq, &size, &stereo, &chunk))
		return NULL;
	if(stereo>=2)
		stereo = 2;
	else
		stereo = 1;

        if(size == 8) size = AUDIO_U8;
        else if(size == -8) size = AUDIO_S8;
        else if(size == 16) size = AUDIO_U16SYS;
        else if(size == -16) size = AUDIO_S16SYS;

	/*make chunk a power of 2*/
	for(i=0; 1<<i < chunk; ++i); //yes, semicolon on for loop
	chunk = max(1<<i, 256);

	if(!SDL_WasInit(SDL_INIT_AUDIO))
	{
		PyGame_RegisterQuit(autoquit);

                if(!channeldata) /*should always be null*/
                {
                    numchanneldata = MIX_CHANNELS;
                    channeldata = (struct ChannelData*)malloc(
			    sizeof(struct ChannelData)*numchanneldata);
                    for(i=0; i < numchanneldata; ++i)
		    {
                        channeldata[i].sound = NULL;
			channeldata[i].queue = NULL;
			channeldata[i].endevent = 0;
		    }
                }

		if(SDL_InitSubSystem(SDL_INIT_AUDIO) == -1)
			return PyInt_FromLong(0);

		if(Mix_OpenAudio(freq, (Uint16)size, stereo, chunk) == -1)
		{
			SDL_QuitSubSystem(SDL_INIT_AUDIO);
			return PyInt_FromLong(0);
		}
#if MIX_MAJOR_VERSION>=1 && MIX_MINOR_VERSION>=2 && MIX_PATCHLEVEL>=3
                Mix_ChannelFinished(endsound_callback);
#endif

              	Mix_VolumeMusic(127);
	}
	return PyInt_FromLong(1);
}


    /*DOC*/ static char doc_quit[] =
    /*DOC*/    "pygame.mixer.quit() -> None\n"
    /*DOC*/    "unitializes the mixer\n"
    /*DOC*/    "\n"
    /*DOC*/    "This will stop all playing sounds and uninitialize\n"
    /*DOC*/    "the mixer module\n"
    /*DOC*/ ;

static PyObject* quit(PyObject* self, PyObject* arg)
{
	if(!PyArg_ParseTuple(arg, ""))
		return NULL;

	autoquit();

	RETURN_NONE
}


    /*DOC*/ static char doc_init[] =
    /*DOC*/    "pygame.mixer.init([freq, [size, [stereo, [buffersize]]]]) -> None\n"
    /*DOC*/    "initialize mixer module\n"
    /*DOC*/    "\n"
    /*DOC*/    "Initializes the mixer module. Usually no arguments will be\n"
    /*DOC*/    "needed, the defaults are 22050 frequency data in stereo with\n"
    /*DOC*/    "signed 16bit data. The size argument can be 8 or 16 for unsigned\n"
    /*DOC*/    "data, or -8 or -16 for signed data. The default buffersize is\n"
    /*DOC*/    "1024 samples, sometimes a larger value is required.\n"
    /*DOC*/    "\n"
    /*DOC*/    "The stereo argument is either 1 or 2 to represent mono or\n"
    /*DOC*/    "stereo. Pygame does not support more than 2 channel stereo.\n"
    /*DOC*/    "\n"
    /*DOC*/    "On some platforms it is important that the display module is\n"
    /*DOC*/    "initialized before the audio. (that is, if the display will be\n"
    /*DOC*/    "initialized at all). You can easily use the pygame.init()\n"
    /*DOC*/    "function to cleanly initialize everything, but first use the\n"
    /*DOC*/    "pygame.mixer.pre_init() function to change the default values for\n"
    /*DOC*/    "this init().\n"
    /*DOC*/ ;

static PyObject* init(PyObject* self, PyObject* arg)
{
	PyObject* result;
	int value;

	result = autoinit(self, arg);
	if(!result)
		return NULL;
	value = PyObject_IsTrue(result);
	Py_DECREF(result);
	if(!value)
		return RAISE(PyExc_SDLError, SDL_GetError());

	RETURN_NONE
}


    /*DOC*/ static char doc_get_init[] =
    /*DOC*/    "pygame.mixer.get_init() -> (frequency,format,stereo)\n"
    /*DOC*/    "query initialization for the mixer\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns a tuple containing the initialized state of the mixer\n"
    /*DOC*/    "module. If the module has not been initialized, it will return\n"
    /*DOC*/    "None.\n"
    /*DOC*/    "\n"
    /*DOC*/ ;

static PyObject* get_init(PyObject* self, PyObject* arg)
{
	int freq, channels, realform;
	Uint16 format;

	if(!PyArg_ParseTuple(arg, ""))
		return NULL;

	if(!SDL_WasInit(SDL_INIT_AUDIO))
		RETURN_NONE

	if(!Mix_QuerySpec(&freq, &format, &channels))
		RETURN_NONE

	//create a signed or unsigned number of bits per sample
	realform = format&~0xff ? -(format&0xff) : format&0xff;
	return Py_BuildValue("(iii)", freq, realform, channels>1);
}



    /*DOC*/ static char doc_pre_init[] =
    /*DOC*/    "pygame.mixer.pre_init([freq, [size, [stereo, [buffersize]]]]) -> None\n"
    /*DOC*/    "presets the init default values\n"
    /*DOC*/    "\n"
    /*DOC*/    "This routine is usefull when you want to customize the sound\n"
    /*DOC*/    "mixer playback modes. The values you pass will change the default\n"
    /*DOC*/    "values used by pygame.mixer.init(). This way you can still use\n"
    /*DOC*/    "the pygame automatic initialization to ensure everything happens\n"
    /*DOC*/    "in the right order, but set the desired mixer mode.\n"
    /*DOC*/    "\n"
    /*DOC*/    "The stereo argument is either 1 or 2 to represent mono or\n"
    /*DOC*/    "stereo. Pygame does not support more than 2 channel stereo.\n"
    /*DOC*/ ;

static PyObject* pre_init(PyObject* self, PyObject* arg)
{
	request_frequency = MIX_DEFAULT_FREQUENCY;
	request_size = MIX_DEFAULT_FORMAT;
	request_stereo = MIX_DEFAULT_CHANNELS;
	request_chunksize = MIX_DEFAULT_CHUNKSIZE;

	if(!PyArg_ParseTuple(arg, "|iiii", &request_frequency, &request_size,
				&request_stereo, &request_chunksize))
		return NULL;

	RETURN_NONE
}




/* sound object methods */

    /*DOC*/ static char doc_snd_play[] =
    /*DOC*/    "Sound.play([loops, [maxtime]]) -> Channel\n"
    /*DOC*/    "play sound\n"
    /*DOC*/    "\n"
    /*DOC*/    "Starts playing a song on an available channel. If no channels are\n"
    /*DOC*/    "available, it will not play and return None. Loops controls how\n"
    /*DOC*/    "many extra times the sound will play, a negative loop will play\n"
    /*DOC*/    "indefinitely, it defaults to 0. Maxtime is the number of total\n"
    /*DOC*/    "milliseconds that the sound will play. It defaults to forever\n"
    /*DOC*/    "(-1).\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns a channel object for the channel that is selected to play\n"
    /*DOC*/    "the sound.\n"
    /*DOC*/ ;

static PyObject* snd_play(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	int channelnum = -1;
	int loops = 0, playtime = -1;

	if(!PyArg_ParseTuple(args, "|ii", &loops, &playtime))
		return NULL;

	channelnum = Mix_PlayChannelTimed(-1, chunk, loops, playtime);
	if(channelnum == -1)
		RETURN_NONE

        Py_XDECREF(channeldata[channelnum].sound);
	Py_XDECREF(channeldata[channelnum].queue);
	channeldata[channelnum].queue = NULL;
        channeldata[channelnum].sound = self;
        Py_INCREF(self);

	//make sure volume on this arbitrary channel is set to full
	Mix_Volume(channelnum, 128);

	Mix_GroupChannel(channelnum, (int)chunk);
	return PyChannel_New(channelnum);
}




    /*DOC*/ static char doc_snd_get_num_channels[] =
    /*DOC*/    "Sound.get_num_channels() -> int\n"
    /*DOC*/    "number of channels with sound\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns the number of channels that have been using this sound.\n"
    /*DOC*/    "The channels may have already finished, but have not started\n"
    /*DOC*/    "playing any other sounds.\n"
    /*DOC*/ ;

static PyObject* snd_get_num_channels(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	return PyInt_FromLong(Mix_GroupCount((int)chunk));
}


#if 0
static char docXX_snd_get_busy[] =
	"Sound.get_busy() -> int\n"
	"Returns the number of channels this sound is actively playing on";

static PyObject* snd_get_busy(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	/*there is no call for this in sdl_mixer, yargh */
	return PyInt_FromLong(Mix_PlayingGroup((int)chunk));
}
#endif


#if 0
static char docXX_snd_get_channel[] =
	"Sound.get_channel(int) -> Channel\n"
	"Retrieves a channel index for this sound";

static PyObject* snd_get_channel(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	int chan;
	if(!PyArg_ParseTuple(args, "i", &chan))
		return NULL;

	MIXER_INIT_CHECK();

	return PyChannel_New(chan);
}
#endif




    /*DOC*/ static char doc_snd_fadeout[] =
    /*DOC*/    "Sound.fadeout(millisec) -> None\n"
    /*DOC*/    "fadeout all channels playing this sound\n"
    /*DOC*/    "\n"
    /*DOC*/    "Fade out all the playing channels playing this sound over the.\n"
    /*DOC*/    "All channels playing this sound will be stopped after the given\n"
    /*DOC*/    "milliseconds.\n"
    /*DOC*/ ;

static PyObject* snd_fadeout(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	int time;
	if(!PyArg_ParseTuple(args, "i", &time))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_FadeOutGroup((int)chunk, time);
	RETURN_NONE
}


    /*DOC*/ static char doc_snd_stop[] =
    /*DOC*/    "Sound.stop() -> None\n"
    /*DOC*/    "stop all channels playing this sound\n"
    /*DOC*/    "\n"
    /*DOC*/    "This will instantly stop all channels playing this sound.\n"
    /*DOC*/ ;

static PyObject* snd_stop(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_HaltGroup((int)chunk);
	RETURN_NONE
}


    /*DOC*/ static char doc_snd_set_volume[] =
    /*DOC*/    "Sound.set_volume(val) -> None\n"
    /*DOC*/    "change volume for sound\n"
    /*DOC*/    "\n"
    /*DOC*/    "Set the play volume for this sound. This will effect any channels\n"
    /*DOC*/    "currently playing this sound, along with all subsequent calls to\n"
    /*DOC*/    "play. The value is 0.0 to 1.0.\n"

    /*DOC*/ ;

static PyObject* snd_set_volume(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	float volume;

	if(!PyArg_ParseTuple(args, "f", &volume))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_VolumeChunk(chunk, (int)(volume*128));
	RETURN_NONE
}


    /*DOC*/ static char doc_snd_get_volume[] =
    /*DOC*/    "Sound.get_volume() -> val\n"
    /*DOC*/    "query volume for sound\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns the current volume for this sound object. The value is\n"
    /*DOC*/    "0.0 to 1.0.\n"
    /*DOC*/ ;

static PyObject* snd_get_volume(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	int volume;

	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	volume = Mix_VolumeChunk(chunk, -1);
	return PyFloat_FromDouble(volume / 128.0);
}


    /*DOC*/ static char doc_snd_get_length[] =
    /*DOC*/    "Sound.get_length() -> float\n"
    /*DOC*/    "get the length of the Sound in seconds.\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns the number of seconds this Sound file has\n"
    /*DOC*/    "of data.\n"
    /*DOC*/ ;

static PyObject* snd_get_length(PyObject* self, PyObject* args)
{
	Mix_Chunk* chunk = PySound_AsChunk(self);
	int freq, channels, mixerbytes, numsamples;
	Uint16 format;

	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_QuerySpec(&freq, &format, &channels);
        if(format==AUDIO_S8 || format==AUDIO_U8)
            mixerbytes = 1;
        else
            mixerbytes = 2;
        numsamples = chunk->alen / mixerbytes / channels;

	return PyFloat_FromDouble((float)numsamples / (float)freq);
}



static PyMethodDef sound_methods[] =
{
	{ "play", snd_play, 1, doc_snd_play },
	{ "get_num_channels", snd_get_num_channels, 1, doc_snd_get_num_channels },

/*	{ "get_busy", snd_get_busy, doc_snd_get_busy }, */
/*	{ "get_channel", snd_get_channel doc_snd_get_channel }, */
	{ "fadeout", snd_fadeout, 1, doc_snd_fadeout },
	{ "stop", snd_stop, 1, doc_snd_stop },
	{ "set_volume", snd_set_volume, 1, doc_snd_set_volume },
	{ "get_volume", snd_get_volume, 1, doc_snd_get_volume },

        { "get_length", snd_get_length, 1, doc_snd_get_length },
        
	{ NULL, NULL }
};


/*sound object internals*/

static void sound_dealloc(PySoundObject* self)
{
    	Mix_Chunk* chunk = PySound_AsChunk((PyObject*)self);
        if(chunk)
            Mix_FreeChunk(chunk);
        if(self->weakreflist)
            PyObject_ClearWeakRefs((PyObject*)self);
	self->ob_type->tp_free((PyObject*)self);
}



    /*DOC*/ static char doc_Sound_MODULE[] =
    /*DOC*/    "Sound objects represent actual sound data. Sound objects are\n"
    /*DOC*/    "created from the function pygame.mixer.Sound(). Sound objects can\n"
    /*DOC*/    "be playing on multiple channels simultaneously. Calling functions\n"
    /*DOC*/    "like Sound.stop() from the sound objects will effect all channels\n"
    /*DOC*/    "playing that Sound object.\n"
    /*DOC*/    "\n"
    /*DOC*/    "All sound objects have the same frequency and format as the\n"
    /*DOC*/    "pygame.mixer module's initialization.\n"
    /*DOC*/ ;

static PyTypeObject PySound_Type =
{
	PyObject_HEAD_INIT(NULL)
	0,
	"pygame.mixer.Sound",
	sizeof(PySoundObject),
	0,
	(destructor)sound_dealloc,
	0,
	NULL,
	NULL,					/*setattr*/
	NULL,					/*compare*/
	NULL,					/*repr*/
	NULL,					/*as_number*/
	NULL,					/*as_sequence*/
	NULL,					/*as_mapping*/
	(hashfunc)NULL, 		/*hash*/
	(ternaryfunc)NULL,		/*call*/
	(reprfunc)NULL, 		/*str*/
	0L,0L,0L,
    	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
	doc_Sound_MODULE, /* Documentation string */
	0,					/* tp_traverse */
	0,					/* tp_clear */
	0,					/* tp_richcompare */
	offsetof(PySoundObject, weakreflist),    /* tp_weaklistoffset */
	0,					/* tp_iter */
	0,					/* tp_iternext */
	sound_methods,			        /* tp_methods */
	0,				        /* tp_members */
	0,				        /* tp_getset */
	0,					/* tp_base */
	0,					/* tp_dict */
	0,					/* tp_descr_get */
	0,					/* tp_descr_set */
	0,					/* tp_dictoffset */
	sound_init,			/* tp_init */
	0,					/* tp_alloc */
	0,	                /* tp_new */
};

	//PyType_GenericNew,	                /* tp_new */



/* channel object methods */


    /*DOC*/ static char doc_chan_play[] =
    /*DOC*/    "Channel.play(Sound, [loops, [maxtime]]) -> None\n"
    /*DOC*/    "play a sound on this channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Starts playing a given sound on this channel. If the channels is\n"
    /*DOC*/    "currently playing a different sound, it will be\n"
    /*DOC*/    "replaced/restarted with the given sound. Loops controls how many\n"
    /*DOC*/    "extra times the sound will play, a negative loop will play\n"
    /*DOC*/    "indefinitely, it defaults to 0. Maxtime is the number of\n"
    /*DOC*/    "totalmilliseconds that the sound will play. It defaults to\n"
    /*DOC*/    "forever (-1).\n"
    /*DOC*/ ;

static PyObject* chan_play(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	PyObject* sound;
	Mix_Chunk* chunk;
	int loops = 0, playtime = -1;

	if(!PyArg_ParseTuple(args, "O!|ii", &PySound_Type, &sound, &loops, &playtime))
		return NULL;
	chunk = PySound_AsChunk(sound);

	channelnum = Mix_PlayChannelTimed(channelnum, chunk, loops, playtime);
	if(channelnum != -1)
		Mix_GroupChannel(channelnum, (int)chunk);

        Py_XDECREF(channeldata[channelnum].sound);
	Py_XDECREF(channeldata[channelnum].queue);
        channeldata[channelnum].sound = sound;
	channeldata[channelnum].queue = NULL;
        Py_INCREF(sound);


	RETURN_NONE
}



    /*DOC*/ static char doc_chan_queue[] =
    /*DOC*/    "Channel.queue(Sound) -> None\n"
    /*DOC*/    "queue a sound on this channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "When you queue a sound on a channel, it will begin playing\n"
    /*DOC*/    "immediately when the current playing sound finishes. Each\n"
    /*DOC*/    "channel can only have a single Sound object queued. The\n"
    /*DOC*/    "queued sound will only play when the current Sound finishes\n"
    /*DOC*/    "naturally, not from another call to stop() or play().\n"
    /*DOC*/    "\n"
    /*DOC*/    "If there is no currently playing sound on this Channel\n"
    /*DOC*/    "it will begin playback immediately.\n"
    /*DOC*/    "\n"
    /*DOC*/    "This will only work with SDL_mixer greater than version 1.2.3\n"
    /*DOC*/ ;

static PyObject* chan_queue(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	PyObject* sound;
	Mix_Chunk* chunk;

	if(!PyArg_ParseTuple(args, "O!", &PySound_Type, &sound))
		return NULL;
	chunk = PySound_AsChunk(sound);

	if(!channeldata[channelnum].sound) /*nothing playing*/
	{
	    channelnum = Mix_PlayChannelTimed(channelnum, chunk, 0, -1);
	    if(channelnum != -1)
		    Mix_GroupChannel(channelnum, (int)chunk);

            channeldata[channelnum].sound = sound;
            Py_INCREF(sound);
	}
	else
	{
	    Py_XDECREF(channeldata[channelnum].queue);
	    channeldata[channelnum].queue = sound;
	    Py_INCREF(sound);
	}

	RETURN_NONE
}




    /*DOC*/ static char doc_chan_get_busy[] =
    /*DOC*/    "Channel.get_busy() -> bool\n"
    /*DOC*/    "query state of the channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns true when there is a sound actively playing on this\n"
    /*DOC*/    "channel.\n"
    /*DOC*/ ;

static PyObject* chan_get_busy(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	return PyInt_FromLong(Mix_Playing(channelnum));
}



    /*DOC*/ static char doc_chan_fadeout[] =
    /*DOC*/    "Channel.fadeout(millisec) -> None\n"
    /*DOC*/    "fade out the channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Fade out the playing sound and stops it over the\n"
    /*DOC*/    "given milliseconds.\n"
    /*DOC*/ ;

static PyObject* chan_fadeout(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	int time;
	if(!PyArg_ParseTuple(args, "i", &time))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_FadeOutChannel(channelnum, time);
	RETURN_NONE
}


    /*DOC*/ static char doc_chan_stop[] =
    /*DOC*/    "Channel.stop() -> None\n"
    /*DOC*/    "stop playing on the channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Stops the sound that is playing on this channel.\n"
    /*DOC*/ ;

static PyObject* chan_stop(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_HaltChannel(channelnum);
	RETURN_NONE
}


    /*DOC*/ static char doc_chan_pause[] =
    /*DOC*/    "Channel.pause() -> None\n"
    /*DOC*/    "temporarily stop the channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Stops the sound that is playing on this channel,\n"
    /*DOC*/    "but it can be resumed with a call to unpause()\n"
    /*DOC*/ ;

static PyObject* chan_pause(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_Pause(channelnum);
	RETURN_NONE
}


    /*DOC*/ static char doc_chan_unpause[] =
    /*DOC*/    "Channel.unpause() -> None\n"
    /*DOC*/    "restart a paused channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Restarts a paused channel where it was paused.\n"
    /*DOC*/ ;

static PyObject* chan_unpause(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_Resume(channelnum);
	RETURN_NONE
}


    /*DOC*/ static char doc_chan_set_volume[] =
    /*DOC*/    "Channel.set_volume(val, [stereoval]) -> None\n"
    /*DOC*/    "set volume for channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Sets the volume for the channel. The channel's volume level is\n"
    /*DOC*/    "mixed with the volume for the active sound object. The value is\n"
    /*DOC*/    "between 0.0 and 1.0.\n"
    /*DOC*/    "\n"
    /*DOC*/    "If mixer is using stereo, you can set the panning for audio\n"
    /*DOC*/    "by supplying a volume for the left and right channels. If\n"
    /*DOC*/    "SDL_mixer cannot set the panning, it will average the two\n"
    /*DOC*/    "volumes. Panning requires SDL_mixer-1.2.1.\n"
    /*DOC*/    "\n"
    /*DOC*/    "NOTE: if the second argument is used then get_volume()\n"
    /*DOC*/    " will not work how you think. It will always return 1.0.\n"
    /*DOC*/ ;

static PyObject* chan_set_volume(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	float volume, stereovolume=-1.11f;
        int result;

	if(!PyArg_ParseTuple(args, "f|f", &volume, &stereovolume))
		return NULL;

	MIXER_INIT_CHECK();
#if MIX_MAJOR_VERSION>=1 && MIX_MINOR_VERSION>=2 && MIX_PATCHLEVEL>=1
        if((stereovolume <= -1.10f) && (stereovolume >= -1.12f)) {
            // The normal volume will be used.  No panning.  so panning is set to full.
            //   this is incase it was set previously to something else.
            // NOTE: there is no way to GetPanning variables.
            result = Mix_SetPanning(channelnum, (Uint8)255, (Uint8)255);
            
        } else {
            // NOTE: here the volume will be set to 1.0 and the panning will be used.
            result = Mix_SetPanning(channelnum, (Uint8)(volume*255), (Uint8)(stereovolume*255));
            volume = 1.0f;
        }
#else
        if(! ((stereovolume <= -1.10f) && (stereovolume >= -1.12f)))
            volume = (volume + stereovolume) * 0.5f;
#endif

	result = Mix_Volume(channelnum, (int)(volume*128));

	RETURN_NONE
}


    /*DOC*/ static char doc_chan_get_volume[] =
    /*DOC*/    "Channel.get_volume() -> val\n"
    /*DOC*/    "query the volume for the\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns the current volume for this sound object. The value is\n"
    /*DOC*/    "between 0.0 and 1.0.\n"
    /*DOC*/ ;

static PyObject* chan_get_volume(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	int volume;

	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	volume = Mix_Volume(channelnum, -1);

	return PyFloat_FromDouble(volume / 128.0);
}




    /*DOC*/ static char doc_chan_get_sound[] =
    /*DOC*/    "Channel.get_sound() -> Sound\n"
    /*DOC*/    "get the currently playing sound object\n"
    /*DOC*/    "\n"
    /*DOC*/    "Return the currently playing Sound object on this channel.\n"
    /*DOC*/    "This will return None if there is nothing playing.\n"
    /*DOC*/ ;

static PyObject* chan_get_sound(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	PyObject* sound;

	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	sound = channeldata[channelnum].sound;
	if(!sound)
	    RETURN_NONE

	Py_INCREF(sound);
	return sound;
}



    /*DOC*/ static char doc_chan_get_queue[] =
    /*DOC*/    "Channel.get_queue() -> Sound\n"
    /*DOC*/    "get the currently queued sound object\n"
    /*DOC*/    "\n"
    /*DOC*/    "Return the currently queued Sound object on this channel.\n"
    /*DOC*/    "This will return None if there is nothing queued.\n"
    /*DOC*/ ;

static PyObject* chan_get_queue(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	PyObject* sound;

	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	sound = channeldata[channelnum].queue;
	if(!sound)
	    RETURN_NONE

	Py_INCREF(sound);
	return sound;
}





    /*DOC*/ static char doc_chan_set_endevent[] =
    /*DOC*/    "Channel.set_endevent([event_type]) -> None\n"
    /*DOC*/    "set an endevent for a channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "When you set an endevent for a channel, that event type\n"
    /*DOC*/    "will be put on the pygame event queue everytime a sound stops\n"
    /*DOC*/    "playing on that channel. This is slightly different than the\n"
    /*DOC*/    "music object end event, because this will trigger an event\n"
    /*DOC*/    "anytime the music stops. If you call stop() or play() on the\n"
    /*DOC*/    "channel, it will fire an event. An event will also be fired when\n"
    /*DOC*/    "playback switches to a queued Sound.\n"
    /*DOC*/    "\n"
    /*DOC*/    "Pass no argument to stop this channel from firing events\n"
    /*DOC*/ ;

static PyObject* chan_set_endevent(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);
	int event = SDL_NOEVENT;

	if(!PyArg_ParseTuple(args, "|i", &event))
		return NULL;

	channeldata[channelnum].endevent = event;
    	RETURN_NONE
}


    /*DOC*/ static char doc_chan_get_endevent[] =
    /*DOC*/    "Channel.get_endevent() -> event_type\n"
    /*DOC*/    "get the endevent for a channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns the end event type for this Channel. If the\n"
    /*DOC*/    "return value is NOEVENT, then no events will be sent\n"
    /*DOC*/    "when playback ends.\n"
    /*DOC*/ ;

static PyObject* chan_get_endevent(PyObject* self, PyObject* args)
{
	int channelnum = PyChannel_AsInt(self);

	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	return PyInt_FromLong(channeldata[channelnum].endevent);
}




static PyMethodDef channel_builtins[] =
{
	{ "play", chan_play, 1, doc_chan_play },
	{ "queue", chan_queue, 1, doc_chan_queue },
	{ "get_busy", chan_get_busy, 1, doc_chan_get_busy },
	{ "fadeout", chan_fadeout, 1, doc_chan_fadeout },
	{ "stop", chan_stop, 1, doc_chan_stop },
	{ "pause", chan_pause, 1, doc_chan_pause },
	{ "unpause", chan_unpause, 1, doc_chan_unpause },
	{ "set_volume", chan_set_volume, 1, doc_chan_set_volume },
	{ "get_volume", chan_get_volume, 1, doc_chan_get_volume },

	{ "get_sound", chan_get_sound, 1, doc_chan_get_sound },
	{ "get_queue", chan_get_queue, 1, doc_chan_get_queue },

	{ "set_endevent", chan_set_endevent, 1, doc_chan_set_endevent },
	{ "get_endevent", chan_get_endevent, 1, doc_chan_get_endevent },

	{ NULL, NULL }
};


/* channel object internals */

static void channel_dealloc(PyObject* self)
{
	PyObject_DEL(self);
}


static PyObject* channel_getattr(PyObject* self, char* attrname)
{
	return Py_FindMethod(channel_builtins, self, attrname);
}


    /*DOC*/ static char doc_Channel_MODULE[] =
    /*DOC*/    "Channel objects represent a single channel of sound. Each channel\n"
    /*DOC*/    "can only playback one Sound object at a time. If your application\n"
    /*DOC*/    "only requires simply sound playback, you will usually not need to\n"
    /*DOC*/    "bother with the Channel objects, they exist for finer playback\n"
    /*DOC*/    "control.\n"
    /*DOC*/    "\n"
    /*DOC*/    "Sound objects can be retrieved from the pygame.mixer module with\n"
    /*DOC*/    "functions like pygame.mixer.Channel() and\n"
    /*DOC*/    "pygame.mixer.find_channel(). Also, each time you call\n"
    /*DOC*/    "Sound.play() a Channel object will be returned, representing the\n"
    /*DOC*/    "channel that sound is playing on.\n"
    /*DOC*/ ;


static PyTypeObject PyChannel_Type =
{
	PyObject_HEAD_INIT(NULL)
	0,
	"Channel",
	sizeof(PyChannelObject),
	0,
	channel_dealloc,
	0,
	channel_getattr,
	NULL,					/*setattr*/
	NULL,					/*compare*/
	NULL,					/*repr*/
	NULL,					/*as_number*/
	NULL,					/*as_sequence*/
	NULL,					/*as_mapping*/
	(hashfunc)NULL, 		/*hash*/
	(ternaryfunc)NULL,		/*call*/
	(reprfunc)NULL, 		/*str*/
	0L,0L,0L,0L,
	doc_Channel_MODULE /* Documentation string */
};



/*mixer module methods*/

    /*DOC*/ static char doc_get_num_channels[] =
    /*DOC*/    "pygame.mixer.get_num_channels() -> int\n"
    /*DOC*/    "query the number of channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Gets the current number of channels available for the mixer. This\n"
    /*DOC*/    "value can be changed with set_num_channels(). This value defaults\n"
    /*DOC*/    "to 8 when the mixer is first initialized.\n"
    /*DOC*/ ;

static PyObject* get_num_channels(PyObject* self, PyObject* args)
{
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	return PyInt_FromLong(Mix_GroupCount(-1));
}


    /*DOC*/ static char doc_set_num_channels[] =
    /*DOC*/    "pygame.mixer.set_num_channels(int) -> None\n"
    /*DOC*/    "sets the number of available channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Sets the current number of channels available for the mixer. This\n"
    /*DOC*/    "value defaults to 8 when the mixer is first initialized. If the\n"
    /*DOC*/    "value is decreased, sounds playing in channels above the new\n"
    /*DOC*/    "value will stop.\n"
    /*DOC*/ ;

static PyObject* set_num_channels(PyObject* self, PyObject* args)
{
	int numchans, i;
	if(!PyArg_ParseTuple(args, "i", &numchans))
		return NULL;

	MIXER_INIT_CHECK();
        if(numchans > numchanneldata)
        {
            channeldata= (struct ChannelData*)realloc(channeldata,
		    sizeof(struct ChannelData) * numchans);
            for(i = numchanneldata; i < numchans; ++i)
	    {
		channeldata[i].sound = NULL;
		channeldata[i].queue = NULL;
	    }
            numchanneldata = numchans;
        }

	Mix_AllocateChannels(numchans);
	RETURN_NONE
}


    /*DOC*/ static char doc_set_reserved[] =
    /*DOC*/    "pygame.mixer.set_reserved(numchans) -> None\n"
    /*DOC*/    "reserves first given channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Reserves numchan channels. Reserved channels won't be used when\n"
    /*DOC*/    "a sound is played without using a specific channel object.\n"
    /*DOC*/    "In otherwords, just calling Sound.play() will not use the reserved\n"
    /*DOC*/    "channels. They must implicitly be used with Channel.play().\n"
    /*DOC*/ ;

static PyObject* set_reserved(PyObject* self, PyObject* args)
{
	int numchans;
	if(!PyArg_ParseTuple(args, "i", &numchans))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_ReserveChannels(numchans);
	RETURN_NONE
}


    /*DOC*/ static char doc_get_busy[] =
    /*DOC*/    "pygame.mixer.get_busy() -> int\n"
    /*DOC*/    "query busy channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Returns the number of current active channels. This is not the\n"
    /*DOC*/    "total channels, but the number of channels that are currently\n"
    /*DOC*/    "playing sound.\n"
    /*DOC*/ ;

static PyObject* get_busy(PyObject* self, PyObject* args)
{
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	if(!SDL_WasInit(SDL_INIT_AUDIO))
		return PyInt_FromLong(0);

	return PyInt_FromLong(Mix_Playing(-1));
}


    /*DOC*/ static char doc_Channel[] =
    /*DOC*/    "pygame.mixer.Channel(int) -> Channel\n"
    /*DOC*/    "get channel object\n"
    /*DOC*/    "\n"
    /*DOC*/    "Get a channel object for the given channel. This number must be\n"
    /*DOC*/    "less that the current number of channels.\n"
    /*DOC*/ ;

static PyObject* Channel(PyObject* self, PyObject* args)
{
	int chan;
	if(!PyArg_ParseTuple(args, "i", &chan))
		return NULL;

	MIXER_INIT_CHECK();

	return PyChannel_New(chan);
}



    /*DOC*/ static char doc_find_channel[] =
    /*DOC*/    "pygame.mixer.find_channel([force]) -> Channel\n"
    /*DOC*/    "find an available sound channel\n"
    /*DOC*/    "\n"
    /*DOC*/    "Find a sound channel that is not busy. If the force argument is\n"
    /*DOC*/    "passed as a nonzero number, this will return the channel of the\n"
    /*DOC*/    "longest running sound. If not forced, and there are no available\n"
    /*DOC*/    "channels, returns None.\n"
    /*DOC*/ ;

static PyObject* mixer_find_channel(PyObject* self, PyObject* args)
{
	int chan, force = 0;
	if(!PyArg_ParseTuple(args, "|i", &force))
		return NULL;

	MIXER_INIT_CHECK();

	chan = Mix_GroupAvailable(-1);
	if(chan == -1)
	{
		if(!force)
			RETURN_NONE
		chan = Mix_GroupOldest(-1);
	}
	return PyChannel_New(chan);
}


    /*DOC*/ static char doc_fadeout[] =
    /*DOC*/    "pygame.mixer.fadeout(millisec) -> None\n"
    /*DOC*/    "fadeout all channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Fade out all the playing channels over the given number of\n"
    /*DOC*/    "milliseconds.\n"
    /*DOC*/ ;

static PyObject* mixer_fadeout(PyObject* self, PyObject* args)
{
	int time;
	if(!PyArg_ParseTuple(args, "i", &time))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_FadeOutChannel(-1, time);
	RETURN_NONE
}


    /*DOC*/ static char doc_stop[] =
    /*DOC*/    "pygame.mixer.stop() -> None\n"
    /*DOC*/    "stop all channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Stop the playback on all mixer channels.\n"
    /*DOC*/ ;

static PyObject* mixer_stop(PyObject* self, PyObject* args)
{
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_HaltChannel(-1);
	RETURN_NONE
}


    /*DOC*/ static char doc_pause[] =
    /*DOC*/    "pygame.mixer.pause() -> None\n"
    /*DOC*/    "pause all channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Temporarily stops playback on all the mixer channels.\n"
    /*DOC*/ ;

static PyObject* mixer_pause(PyObject* self, PyObject* args)
{
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_Pause(-1);
	RETURN_NONE
}


    /*DOC*/ static char doc_unpause[] =
    /*DOC*/    "pygame.mixer.unpause() -> None\n"
    /*DOC*/    "restart any pause channels\n"
    /*DOC*/    "\n"
    /*DOC*/    "Restarts playback of any paused channels.\n"
    /*DOC*/ ;

static PyObject* mixer_unpause(PyObject* self, PyObject* args)
{
	if(!PyArg_ParseTuple(args, ""))
		return NULL;

	MIXER_INIT_CHECK();

	Mix_Resume(-1);
	RETURN_NONE
}


#if 0
    /*DOC*/ static char doc_Sound[] =
    /*DOC*/    "pygame.mixer.Sound(file) -> Sound\n"
    /*DOC*/    "load a new soundfile\n"
    /*DOC*/    "\n"
    /*DOC*/    "Loads a new sound object from a WAV file. File can be a filename\n"
    /*DOC*/    "or a file-like object. The sound will be converted to match the\n"
    /*DOC*/    "current mode of the mixer.\n"
    /*DOC*/ ;
#endif
static int sound_init(PyObject* self, PyObject* arg, PyObject* kwarg)
{
	PyObject* file;
	char* name = NULL;
	Mix_Chunk* chunk;
    
        ((PySoundObject*)self)->chunk = NULL;

        if(!PyArg_ParseTuple(arg, "O", &file))
		return -1;

        
	if(!SDL_WasInit(SDL_INIT_AUDIO)) 
        {
		RAISE(PyExc_SDLError, "mixer system not initialized");
                return -1;
        }
        
	if(PyString_Check(file) || PyUnicode_Check(file))
	{
		if(!PyArg_ParseTuple(arg, "s", &name))
			return -1;
		Py_BEGIN_ALLOW_THREADS
		chunk = Mix_LoadWAV(name);
		Py_END_ALLOW_THREADS
	}
	else
	{
                SDL_RWops *rw;
		if(!(rw = RWopsFromPython(file)))
			return -1;
		if(RWopsCheckPython(rw))
			chunk = Mix_LoadWAV_RW(rw, 1);
		else
		{
			Py_BEGIN_ALLOW_THREADS
			chunk = Mix_LoadWAV_RW(rw, 1);
			Py_END_ALLOW_THREADS
		}
	}

	if(!chunk)
        {
		RAISE(PyExc_SDLError, SDL_GetError());
                return -1;
        }
        
        ((PySoundObject*)self)->chunk = chunk;
	return 0;
}




static PyMethodDef mixer_builtins[] =
{
	{ "__PYGAMEinit__", autoinit, 1, doc_init },
	{ "init", init, 1, doc_init },
	{ "quit", quit, 1, doc_quit },
	{ "get_init", get_init, 1, doc_get_init },
	{ "pre_init", pre_init, 1, doc_pre_init },

	{ "get_num_channels", get_num_channels, 1, doc_get_num_channels },
	{ "set_num_channels", set_num_channels, 1, doc_set_num_channels },
	{ "set_reserved", set_reserved, 1, doc_set_reserved },

	{ "get_busy", get_busy, 1, doc_get_busy },
	{ "Channel", Channel, 1, doc_Channel },
	{ "find_channel", mixer_find_channel, 1, doc_find_channel },
	{ "fadeout", mixer_fadeout, 1, doc_fadeout },
	{ "stop", mixer_stop, 1, doc_stop },
	{ "pause", mixer_pause, 1, doc_pause },
	{ "unpause", mixer_unpause, 1, doc_unpause },
/*	{ "lookup_frequency", lookup_frequency, 1, doc_lookup_frequency },*/

	{ NULL, NULL }
};



static PyObject* PySound_New(Mix_Chunk* chunk)
{
	PySoundObject* soundobj;

	if(!chunk)
		return RAISE(PyExc_RuntimeError, "unable to create sound.");

	soundobj = (PySoundObject *)PySound_Type.tp_new(&PySound_Type, NULL, NULL);
	if(soundobj)
		soundobj->chunk = chunk;

	return (PyObject*)soundobj;
}



static PyObject* PyChannel_New(int channelnum)
{
	PyChannelObject* chanobj;

	if(channelnum < 0 || channelnum >= Mix_GroupCount(-1))
		return RAISE(PyExc_IndexError, "invalid channel index");

	chanobj = PyObject_NEW(PyChannelObject, &PyChannel_Type);
	if(!chanobj)
		return NULL;

	chanobj->chan = channelnum;
	return (PyObject*)chanobj;
}



    /*DOC*/ static char doc_pygame_mixer_MODULE[] =
    /*DOC*/    "Contains sound mixer routines and objects. The mixer module is an\n"
    /*DOC*/    "optional pygame module, dependent on the SDL_mixer library. This\n"
    /*DOC*/    "module contains the usual routines needed to initialize the\n"
    /*DOC*/    "module. One difference is the pygame.mixer.init() function takes\n"
    /*DOC*/    "several optional arguments. These arguments control the playback\n"
    /*DOC*/    "rates and datatypes for the sound playback. If you do need\n"
    /*DOC*/    "specific control over the playback rate, but don't want to bother\n"
    /*DOC*/    "with hand-initializing the modules, there is a function named\n"
    /*DOC*/    "pygame.mixer.pre_init() which takes the same arguments as init(),\n"
    /*DOC*/    "but only sets the new default values. You can call this before\n"
    /*DOC*/    "pygame.init() and not have to worry about the pygame module\n"
    /*DOC*/    "initialization order.\n"
    /*DOC*/    "\n"
    /*DOC*/    "Sound objects are created from the pygame.mixer.Sound() function.\n"
    /*DOC*/    "Simple sound playback can simply use the Sound.play() method to\n"
    /*DOC*/    "play the sound. Each Sound object can be played multiple times\n"
    /*DOC*/    "simultaneously. If you desire more specific control over the\n"
    /*DOC*/    "Sound objects, you can access the Channel objects with functions\n"
    /*DOC*/    "like pygame.mixer.Channel().\n"
    /*DOC*/    "\n"
    /*DOC*/    "The mixer defaults to supporting 8 simultaneous soundfiles.\n"
    /*DOC*/    "You can change the number of available sound channels at any\n"
    /*DOC*/    "time with the set_num_channels() function.\n"
    /*DOC*/    "\n"
    /*DOC*/    "All loaded Sound objects are resampled to match the same format\n"
    /*DOC*/    "that pygame.mixer is initialized to. The current SDL resampling\n"
    /*DOC*/    "functions are not that good, so it is best if you initialize\n"
    /*DOC*/    "pygame.mixer to the same format as your sound resources. Also\n"
    /*DOC*/    "setting the mixer frequency to even multiples of your sound\n"
    /*DOC*/    "resources will result in a cleaner conversion.\n"
    /*DOC*/    "\n"
    /*DOC*/    "The mixer also contains a special channel for music. You can\n"
    /*DOC*/    "control the music channel through pygame.mixer.music.\n"
    /*DOC*/ ;

PYGAME_EXPORT
void initmixer(void)
{
	PyObject *module, *dict, *apiobj, *music=NULL;
	static void* c_api[PYGAMEAPI_MIXER_NUMSLOTS];

	PyMIXER_C_API[0] = PyMIXER_C_API[0]; /*this cleans an unused warning*/

        if (PyType_Ready(&PySound_Type) < 0)
            return;
	PyType_Init(PyChannel_Type);

    /* create the module */
        PySound_Type.tp_new = &PyType_GenericNew;
	module = Py_InitModule3("mixer", mixer_builtins, doc_pygame_mixer_MODULE);
	dict = PyModule_GetDict(module);

	PyDict_SetItemString(dict, "Sound", (PyObject *)&PySound_Type);
	PyDict_SetItemString(dict, "SoundType", (PyObject *)&PySound_Type);
	PyDict_SetItemString(dict, "ChannelType", (PyObject *)&PyChannel_Type);

	/* export the c api */
	c_api[0] = &PySound_Type;
	c_api[1] = PySound_New;
	c_api[2] = snd_play;
	c_api[3] = &PyChannel_Type;
	c_api[4] = PyChannel_New;
	c_api[5] = autoinit;
	c_api[6] = autoquit;
	apiobj = PyCObject_FromVoidPtr(c_api, NULL);
	PyDict_SetItemString(dict, PYGAMEAPI_LOCAL_ENTRY, apiobj);

	/*imported needed apis*/
	import_pygame_base();
	import_pygame_rwobject();

	music = PyImport_ImportModule("pygame.mixer_music");
        if(music)
	{
		PyObject* ptr, *dict;
		PyModule_AddObject(module, "music", music);
		dict = PyModule_GetDict(music);
		ptr = PyDict_GetItemString(dict, "_MUSIC_POINTER");
		current_music = (Mix_Music**)PyCObject_AsVoidPtr(ptr);
		ptr = PyDict_GetItemString(dict, "_QUEUE_POINTER");
		queue_music = (Mix_Music**)PyCObject_AsVoidPtr(ptr);
	}
	else /*music module not compiled? cleanly ignore*/
	{
            current_music = NULL;
           PyErr_Clear();
	}
}
