mame/src/emu/sound/samples.c
Aaron Giles 42c9aeff39 Cleaned up device and sound interfaces to match the CPU
interfaces when handling strings. Namely, the generic
get_info functions allocate a temporary string and the
device in question copies its string to the target,
instead of assigning a const char *. Updated all device
and sound cores to operate this way.

Added the concept of a cpu_state_table, which is
supplied by the CPU cores and which describes all the
register state accessible to the debugger and other
subsystems. The format of the table is such that most
data can be simply fetched from memory without the
further involvement of the CPU core, including the 
display of common formats. Extensibility points are
available for custom display and for importing/exporting
the data to intermediate variables for more complicated
scenarios. Updated the ADSP21xx, TMS340x0, and i86 cores
to use this.

Removed the old debugger register list, which was never
used. Replaced it with using ordering from the
cpu_state_table.

Renamed REG_PC -> REG_GENPC, REG_SP -> REG_GENSP, and
REG_PREVIOUSPC -> REG_GENPCBASE. Updated a few spots
that were using these directly. Moved these definitions
into the end of the register area rather than leaving
them outside which put them in a weird range.
2008-12-19 06:46:17 +00:00

615 lines
15 KiB
C

#include "driver.h"
#include "streams.h"
#include "samples.h"
struct sample_channel
{
sound_stream *stream;
const INT16 *source;
INT32 source_length;
INT32 source_num;
UINT32 pos;
UINT32 frac;
UINT32 step;
UINT32 basefreq;
UINT8 loop;
UINT8 paused;
};
struct samples_info
{
const device_config *device;
int numchannels; /* how many channels */
struct sample_channel *channel;/* array of channels */
struct loaded_samples *samples;/* array of samples */
};
#define FRAC_BITS 24
#define FRAC_ONE (1 << FRAC_BITS)
#define FRAC_MASK (FRAC_ONE - 1)
#define MAX_CHANNELS 100
/*-------------------------------------------------
read_wav_sample - read a WAV file as a sample
-------------------------------------------------*/
#ifdef LSB_FIRST
#define intelLong(x) (x)
#else
#define intelLong(x) (((x << 24) | (((unsigned long) x) >> 24) | (( x & 0x0000ff00) << 8) | (( x & 0x00ff0000) >> 8)))
#endif
static int read_wav_sample(mame_file *f, struct loaded_sample *sample)
{
unsigned long offset = 0;
UINT32 length, rate, filesize;
UINT16 bits, temp16;
char buf[32];
#ifndef LSB_FIRST
UINT32 sindex;
#endif
/* read the core header and make sure it's a WAVE file */
offset += mame_fread(f, buf, 4);
if (offset < 4)
return 0;
if (memcmp(&buf[0], "RIFF", 4) != 0)
return 0;
/* get the total size */
offset += mame_fread(f, &filesize, 4);
if (offset < 8)
return 0;
filesize = intelLong(filesize);
/* read the RIFF file type and make sure it's a WAVE file */
offset += mame_fread(f, buf, 4);
if (offset < 12)
return 0;
if (memcmp(&buf[0], "WAVE", 4) != 0)
return 0;
/* seek until we find a format tag */
while (1)
{
offset += mame_fread(f, buf, 4);
offset += mame_fread(f, &length, 4);
length = intelLong(length);
if (memcmp(&buf[0], "fmt ", 4) == 0)
break;
/* seek to the next block */
mame_fseek(f, length, SEEK_CUR);
offset += length;
if (offset >= filesize)
return 0;
}
/* read the format -- make sure it is PCM */
offset += mame_fread(f, &temp16, 2);
temp16 = LITTLE_ENDIANIZE_INT16(temp16);
if (temp16 != 1)
return 0;
/* number of channels -- only mono is supported */
offset += mame_fread(f, &temp16, 2);
temp16 = LITTLE_ENDIANIZE_INT16(temp16);
if (temp16 != 1)
return 0;
/* sample rate */
offset += mame_fread(f, &rate, 4);
rate = intelLong(rate);
/* bytes/second and block alignment are ignored */
offset += mame_fread(f, buf, 6);
/* bits/sample */
offset += mame_fread(f, &bits, 2);
bits = LITTLE_ENDIANIZE_INT16(bits);
if (bits != 8 && bits != 16)
return 0;
/* seek past any extra data */
mame_fseek(f, length - 16, SEEK_CUR);
offset += length - 16;
/* seek until we find a data tag */
while (1)
{
offset += mame_fread(f, buf, 4);
offset += mame_fread(f, &length, 4);
length = intelLong(length);
if (memcmp(&buf[0], "data", 4) == 0)
break;
/* seek to the next block */
mame_fseek(f, length, SEEK_CUR);
offset += length;
if (offset >= filesize)
return 0;
}
/* if there was a 0 length data block, we're done */
if (length == 0)
return 0;
/* fill in the sample data */
sample->length = length;
sample->frequency = rate;
/* read the data in */
if (bits == 8)
{
unsigned char *tempptr;
int sindex;
sample->data = auto_malloc(sizeof(*sample->data) * length);
mame_fread(f, sample->data, length);
/* convert 8-bit data to signed samples */
tempptr = (unsigned char *)sample->data;
for (sindex = length - 1; sindex >= 0; sindex--)
sample->data[sindex] = (INT8)(tempptr[sindex] ^ 0x80) * 256;
}
else
{
/* 16-bit data is fine as-is */
sample->data = auto_malloc(sizeof(*sample->data) * (length/2));
mame_fread(f, sample->data, length);
sample->length /= 2;
#ifndef LSB_FIRST
for (sindex = 0; sindex < sample->length; sindex++)
sample->data[sindex] = LITTLE_ENDIANIZE_INT16(sample->data[sindex]);
#endif
}
return 1;
}
/*-------------------------------------------------
readsamples - load all samples
-------------------------------------------------*/
struct loaded_samples *readsamples(const char *const *samplenames, const char *basename)
{
struct loaded_samples *samples;
int skipfirst = 0;
int i;
/* if the user doesn't want to use samples, bail */
if (!options_get_bool(mame_options(), OPTION_SAMPLES))
return NULL;
if (samplenames == 0 || samplenames[0] == 0)
return NULL;
/* if a name begins with '*', we will also look under that as an alternate basename */
if (samplenames[0][0] == '*')
skipfirst = 1;
/* count the samples */
for (i = 0; samplenames[i+skipfirst] != 0; i++) ;
if (i == 0)
return NULL;
/* allocate the array */
samples = auto_malloc(sizeof(struct loaded_samples) + (i-1) * sizeof(struct loaded_sample));
memset(samples, 0, sizeof(struct loaded_samples) + (i-1) * sizeof(struct loaded_sample));
samples->total = i;
/* load the samples */
for (i = 0; i < samples->total; i++)
if (samplenames[i+skipfirst][0])
{
file_error filerr;
mame_file *f;
astring *fname;
fname = astring_assemble_3(astring_alloc(), basename, PATH_SEPARATOR, samplenames[i+skipfirst]);
filerr = mame_fopen(SEARCHPATH_SAMPLE, astring_c(fname), OPEN_FLAG_READ, &f);
if (filerr != FILERR_NONE && skipfirst)
{
astring_assemble_3(fname, samplenames[0] + 1, PATH_SEPARATOR, samplenames[i+skipfirst]);
filerr = mame_fopen(SEARCHPATH_SAMPLE, astring_c(fname), OPEN_FLAG_READ, &f);
}
if (filerr == FILERR_NONE)
{
read_wav_sample(f, &samples->sample[i]);
mame_fclose(f);
}
astring_free(fname);
}
return samples;
}
/* Start one of the samples loaded from disk. Note: channel must be in the range */
/* 0 .. Samplesinterface->channels-1. It is NOT the discrete channel to pass to */
/* mixer_play_sample() */
void sample_start_n(int num,int channel,int samplenum,int loop)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
struct loaded_sample *sample;
/* if samples are disabled, just return quietly */
if (info->samples == NULL)
return;
assert( samplenum < info->samples->total );
assert( channel < info->numchannels );
chan = &info->channel[channel];
/* force an update before we start */
stream_update(chan->stream);
/* update the parameters */
sample = &info->samples->sample[samplenum];
chan->source = sample->data;
chan->source_length = sample->length;
chan->source_num = sample->data ? samplenum : -1;
chan->pos = 0;
chan->frac = 0;
chan->basefreq = sample->frequency;
chan->step = ((INT64)chan->basefreq << FRAC_BITS) / info->device->machine->sample_rate;
chan->loop = loop;
}
void sample_start(int channel,int samplenum,int loop)
{
sample_start_n(0,channel,samplenum,loop);
}
void sample_start_raw_n(int num,int channel,const INT16 *sampledata,int samples,int frequency,int loop)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
/* force an update before we start */
stream_update(chan->stream);
/* update the parameters */
chan->source = sampledata;
chan->source_length = samples;
chan->source_num = -1;
chan->pos = 0;
chan->frac = 0;
chan->basefreq = frequency;
chan->step = ((INT64)chan->basefreq << FRAC_BITS) / info->device->machine->sample_rate;
chan->loop = loop;
}
void sample_start_raw(int channel,const INT16 *sampledata,int samples,int frequency,int loop)
{
sample_start_raw_n(0,channel,sampledata,samples,frequency,loop);
}
void sample_set_freq_n(int num,int channel,int freq)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
/* force an update before we start */
stream_update(chan->stream);
chan->step = ((INT64)freq << FRAC_BITS) / info->device->machine->sample_rate;
}
void sample_set_freq(int channel,int freq)
{
sample_set_freq_n(0,channel,freq);
}
void sample_set_volume_n(int num,int channel,float volume)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
stream_set_output_gain(chan->stream, 0, volume);
}
void sample_set_volume(int channel,float volume)
{
sample_set_volume_n(0,channel,volume);
}
void sample_set_pause_n(int num,int channel,int pause)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
/* force an update before we start */
stream_update(chan->stream);
chan->paused = pause;
}
void sample_set_pause(int channel,int pause)
{
sample_set_pause_n(0,channel,pause);
}
void sample_stop_n(int num,int channel)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
/* force an update before we start */
stream_update(chan->stream);
chan->source = NULL;
chan->source_num = -1;
}
void sample_stop(int channel)
{
sample_stop_n(0,channel);
}
int sample_get_base_freq_n(int num,int channel)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
/* force an update before we start */
stream_update(chan->stream);
return chan->basefreq;
}
int sample_get_base_freq(int channel)
{
return sample_get_base_freq_n(0,channel);
}
int sample_playing_n(int num,int channel)
{
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
struct sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
/* force an update before we start */
stream_update(chan->stream);
return (chan->source != NULL);
}
int sample_playing(int channel)
{
return sample_playing_n(0,channel);
}
int sample_loaded_n(int num,int samplenum)
{
int ret = 0;
struct samples_info *info = sndti_token(SOUND_SAMPLES, num);
if (info->samples != NULL)
{
assert( samplenum < info->samples->total );
ret = (info->samples->sample[samplenum].data != NULL);
}
return ret;
}
int sample_loaded(int samplenum)
{
return sample_loaded_n(0,samplenum);
}
static STREAM_UPDATE( sample_update_sound )
{
struct sample_channel *chan = param;
stream_sample_t *buffer = outputs[0];
if (chan->source && !chan->paused)
{
/* load some info locally */
UINT32 pos = chan->pos;
UINT32 frac = chan->frac;
UINT32 step = chan->step;
const INT16 *sample = chan->source;
UINT32 sample_length = chan->source_length;
while (samples--)
{
/* do a linear interp on the sample */
INT32 sample1 = sample[pos];
INT32 sample2 = sample[(pos + 1) % sample_length];
INT32 fracmult = frac >> (FRAC_BITS - 14);
*buffer++ = ((0x4000 - fracmult) * sample1 + fracmult * sample2) >> 14;
/* advance */
frac += step;
pos += frac >> FRAC_BITS;
frac = frac & ((1 << FRAC_BITS) - 1);
/* handle looping/ending */
if (pos >= sample_length)
{
if (chan->loop)
pos %= sample_length;
else
{
chan->source = NULL;
chan->source_num = -1;
if (samples > 0)
memset(buffer, 0, samples * sizeof(*buffer));
break;
}
}
}
/* push position back out */
chan->pos = pos;
chan->frac = frac;
}
else
memset(buffer, 0, samples * sizeof(*buffer));
}
static STATE_POSTLOAD( samples_postload )
{
struct samples_info *info = param;
int i;
/* loop over channels */
for (i = 0; i < info->numchannels; i++)
{
struct sample_channel *chan = &info->channel[i];
/* attach any samples that were loaded and playing */
if (chan->source_num >= 0 && chan->source_num < info->samples->total)
{
struct loaded_sample *sample = &info->samples->sample[chan->source_num];
chan->source = sample->data;
chan->source_length = sample->length;
if (!sample->data)
chan->source_num = -1;
}
/* validate the position against the length in case the sample is smaller */
if (chan->source && chan->pos >= chan->source_length)
{
if (chan->loop)
chan->pos %= chan->source_length;
else
{
chan->source = NULL;
chan->source_num = -1;
}
}
}
}
static SND_START( samples )
{
int i;
const samples_interface *intf = config;
struct samples_info *info;
info = auto_malloc(sizeof(*info));
memset(info, 0, sizeof(*info));
info->device = device;
sndintrf_register_token(info);
/* read audio samples */
if (intf->samplenames)
info->samples = readsamples(intf->samplenames,device->machine->gamedrv->name);
/* allocate channels */
info->numchannels = intf->channels;
assert(info->numchannels < MAX_CHANNELS);
info->channel = auto_malloc(sizeof(*info->channel) * info->numchannels);
for (i = 0; i < info->numchannels; i++)
{
info->channel[i].stream = stream_create(device, 0, 1, device->machine->sample_rate, &info->channel[i], sample_update_sound);
info->channel[i].source = NULL;
info->channel[i].source_num = -1;
info->channel[i].step = 0;
info->channel[i].loop = 0;
info->channel[i].paused = 0;
/* register with the save state system */
state_save_register_device_item(device, i, info->channel[i].source_length);
state_save_register_device_item(device, i, info->channel[i].source_num);
state_save_register_device_item(device, i, info->channel[i].pos);
state_save_register_device_item(device, i, info->channel[i].frac);
state_save_register_device_item(device, i, info->channel[i].step);
state_save_register_device_item(device, i, info->channel[i].loop);
state_save_register_device_item(device, i, info->channel[i].paused);
}
state_save_register_postload(device->machine, samples_postload, info);
/* initialize any custom handlers */
if (intf->start)
(*intf->start)(device);
return info;
}
/**************************************************************************
* Generic get_info
**************************************************************************/
static SND_SET_INFO( samples )
{
switch (state)
{
/* no parameters to set */
}
}
SND_GET_INFO( samples )
{
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( samples ); break;
case SNDINFO_PTR_START: info->start = SND_START_NAME( samples ); 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, "Samples"); break;
case SNDINFO_STR_CORE_FAMILY: strcpy(info->s, "Big Hack"); break;
case SNDINFO_STR_CORE_VERSION: strcpy(info->s, "1.1"); 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;
}
}