mame/src/emu/sound/samples.c
Aaron Giles b24198a1cd Hi mamedev,
This is a reworked/expanded version of the patch I sent yesterday.
This one is split into three parts:

1.  This introduces function macros for SAMPLES_START,
CUSTOM_{START,STOP,RESET}, and ANTIC_RENDERER.
2.  This introduces running_machine *machine throughout MAME.
Principally it adds running_machine *machine = Machine to the top of
functions, but in some static functions the parameter is added
directly.  Some similar changes in 99xxcore.h, v9938.c, v9938mod.c,
galaxold.c, psx.c, taito_l.c are also made to eliminate Machine
params.  No global API is changed.
3.  This changes the APIs introduced in the first part to pass device
or space as appropriate.  A few similar changes in some other global
apis are made as well.

The net result of this sequence of patches is to remove 40% of the
Machine references and 27 deprecat.h includes.

~aa
2008-12-08 06:53:40 +00:00

614 lines
15 KiB
C

#include "driver.h"
#include "streams.h"
#include "deprecat.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
{
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) / 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) / 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) / 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 void sample_update_sound(void *param, stream_sample_t **inputs, stream_sample_t **_buffer, int length)
{
struct sample_channel *chan = param;
stream_sample_t *buffer = _buffer[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 (length--)
{
/* 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 (length > 0)
memset(buffer, 0, length * sizeof(*buffer));
break;
}
}
}
/* push position back out */
chan->pos = pos;
chan->frac = frac;
}
else
memset(buffer, 0, length * 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));
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(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: info->s = "Samples"; break;
case SNDINFO_STR_CORE_FAMILY: info->s = "Big Hack"; break;
case SNDINFO_STR_CORE_VERSION: info->s = "1.1"; break;
case SNDINFO_STR_CORE_FILE: info->s = __FILE__; break;
case SNDINFO_STR_CORE_CREDITS: info->s = "Copyright Nicola Salmoria and the MAME Team"; break;
}
}