mirror of
https://github.com/holub/mame
synced 2025-05-19 12:18:56 +03:00

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
614 lines
15 KiB
C
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;
|
|
}
|
|
}
|
|
|