[wrap]
MAME source file: / src / emu / sound / dmadac.c [download] (view on mamedev.org)
/***************************************************************************

    DMA-driven DAC driver
    by Aaron Giles

***************************************************************************/

#include "sndintrf.h"
#include "streams.h"
#include "dmadac.h"



/*************************************
 *
 *  Debugging
 *
 *************************************/

#define VERBOSE		0

#define LOG(x) do { if (VERBOSE) logerror x; } while (0)


/*************************************
 *
 *  Constants
 *
 *************************************/

#define DEFAULT_SAMPLE_RATE			(44100)

#define BUFFER_SIZE					32768



/*************************************
 *
 *  Types
 *
 *************************************/

struct dmadac_channel_data
{
	/* sound stream and buffers */
	sound_stream *	channel;
	INT16 *			buffer;
	UINT32			bufin;
	UINT32			bufout;

	/* per-channel parameters */
	INT16			volume;
	UINT8			enabled;
	double			frequency;
};



/*************************************
 *
 *  Stream callback
 *
 *************************************/

static STREAM_UPDATE( dmadac_update )
{
	struct dmadac_channel_data *ch = param;
	stream_sample_t *output = outputs[0];
	INT16 *source = ch->buffer;
	UINT32 curout = ch->bufout;
	UINT32 curin = ch->bufin;
	int volume = ch->volume;

	/* feed as much as we can */
	while (curout != curin && samples-- > 0)
		*output++ = (source[curout++ % BUFFER_SIZE] * volume) >> 8;

	/* fill the rest with silence */
	while (samples-- > 0)
		*output++ = 0;

	/* save the new output pointer */
	ch->bufout = curout % BUFFER_SIZE;
}



/*************************************
 *
 *  Sound hardware init
 *
 *************************************/

static SND_START( dmadac )
{
	struct dmadac_channel_data *info;

	info = auto_malloc(sizeof(*info));
	memset(info, 0, sizeof(*info));

	/* allocate a clear a buffer */
	info->buffer = auto_malloc(sizeof(info->buffer[0]) * BUFFER_SIZE);
	memset(info->buffer, 0, sizeof(info->buffer[0]) * BUFFER_SIZE);

	/* reset the state */
	info->volume = 0x100;

	/* allocate a stream channel */
	info->channel = stream_create(device, 0, 1, DEFAULT_SAMPLE_RATE, info, dmadac_update);

	/* register with the save state system */
	state_save_register_device_item(device, 0, info->bufin);
	state_save_register_device_item(device, 0, info->bufout);
	state_save_register_device_item(device, 0, info->volume);
	state_save_register_device_item(device, 0, info->enabled);
	state_save_register_device_item(device, 0, info->frequency);
	state_save_register_device_item_pointer(device, 0, info->buffer, BUFFER_SIZE);

	return info;
}



/*************************************
 *
 *  Primary transfer routine
 *
 *************************************/

void dmadac_transfer(UINT8 first_channel, UINT8 num_channels, offs_t channel_spacing, offs_t frame_spacing, offs_t total_frames, INT16 *data)
{
	int i, j;

	/* flush out as much data as we can */
	for (i = 0; i < num_channels; i++)
	{
		struct dmadac_channel_data *info = sndti_token(SOUND_DMADAC, first_channel + i);
		stream_update(info->channel);
	}

	/* loop over all channels and accumulate the data */
	for (i = 0; i < num_channels; i++)
	{
		struct dmadac_channel_data *ch = sndti_token(SOUND_DMADAC, first_channel + i);
		if (ch->enabled)
		{
			int maxin = (ch->bufout + BUFFER_SIZE - 1) % BUFFER_SIZE;
			INT16 *src = data + i * channel_spacing;
			int curin = ch->bufin;

			/* copy the data */
			for (j = 0; j < total_frames && curin != maxin; j++)
			{
				ch->buffer[curin++ % BUFFER_SIZE] = *src;
				src += frame_spacing;
			}
			ch->bufin = curin;

			/* log overruns */
			if (j != total_frames)
				logerror("dmadac_transfer: buffer overrun (short %d frames)\n", total_frames - j);
		}
	}

	//LOG(("dmadac_transfer - %d samples, %d effective, %d in buffer\n", total_frames, (int)(total_frames * (double)DEFAULT_SAMPLE_RATE / dmadac[first_channel].frequency), dmadac[first_channel].curinpos - dmadac[first_channel].curoutpos));
}



/*************************************
 *
 *  Enable/disable DMA channel(s)
 *
 *************************************/

void dmadac_enable(UINT8 first_channel, UINT8 num_channels, UINT8 enable)
{
	int i;

	/* flush out as much data as we can */
	for (i = 0; i < num_channels; i++)
	{
		struct dmadac_channel_data *info = sndti_token(SOUND_DMADAC, first_channel + i);
		stream_update(info->channel);
		info->enabled = enable;
		if (!enable)
			info->bufin = info->bufout = 0;
	}
}



/*************************************
 *
 *  Set the frequency on DMA channel(s)
 *
 *************************************/

void dmadac_set_frequency(UINT8 first_channel, UINT8 num_channels, double frequency)
{
	int i;

	/* set the sample rate on each channel */
	for (i = 0; i < num_channels; i++)
	{
		struct dmadac_channel_data *info = sndti_token(SOUND_DMADAC, first_channel + i);
		stream_set_sample_rate(info->channel, frequency);
	}
}



/*************************************
 *
 *  Set the volume on DMA channel(s)
 *
 *************************************/

void dmadac_set_volume(UINT8 first_channel, UINT8 num_channels, UINT16 volume)
{
	int i;

	/* flush out as much data as we can */
	for (i = 0; i < num_channels; i++)
	{
		struct dmadac_channel_data *info = sndti_token(SOUND_DMADAC, first_channel + i);
		stream_update(info->channel);
		info->volume = volume;
	}
}



/**************************************************************************
 * Generic get_info
 **************************************************************************/

static SND_SET_INFO( dmadac )
{
	switch (state)
	{
		/* no parameters to set */
	}
}


SND_GET_INFO( dmadac )
{
	switch (state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case SNDINFO_PTR_SET_INFO:						info->set_info = SND_SET_INFO_NAME( dmadac );	break;
		case SNDINFO_PTR_START:							info->start = SND_START_NAME( dmadac );			break;
		case SNDINFO_PTR_STOP:							/* nothing */									break;
		case SNDINFO_PTR_RESET:							/* nothing */									break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case SNDINFO_STR_NAME:							strcpy(info->s, "DMA-driven DAC");				break;
		case SNDINFO_STR_CORE_FAMILY:					strcpy(info->s, "DAC");							break;
		case SNDINFO_STR_CORE_VERSION:					strcpy(info->s, "1.0");							break;
		case SNDINFO_STR_CORE_FILE:						strcpy(info->s, __FILE__);						break;
		case SNDINFO_STR_CORE_CREDITS:					strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
	}
}

  
2004-2009 MAWS all copyrights belong to their respective owners