mame/src/emu/sound/samples.c
Aaron Giles ad4910a8a8 Bulk change alert.
This update changes the way we handle memory allocation. Rather
than allocating in terms of bytes, allocations are now done in
terms of objects. This is done via new set of macros that replace
the malloc_or_die() macro:

  alloc_or_die(t) - allocate memory for an object of type 't'
  alloc_array_or_die(t,c) - allocate memory for an array of 'c' objects of type 't'
  alloc_clear_or_die(t) - same as alloc_or_die but memset's the memory to 0
  alloc_array_clear_or_die(t,c) - same as alloc_array_or_die but memset's the memory to 0

All original callers of malloc_or_die have been updated to call these
new macros. If you just need an array of bytes, you can use
alloc_array_or_die(UINT8, numbytes).

Made a similar change to the auto_* allocation macros. In addition,
added 'machine' as a required parameter to the auto-allocation macros,
as the resource pools will eventually be owned by the machine object.
The new macros are:

  auto_alloc(m,t) - allocate memory for an object of type 't'
  auto_alloc_array(m,t,c) - allocate memory for an array of 'c' objects of type 't'
  auto_alloc_clear(m,t) - allocate and memset
  auto_alloc_array_clear(m,t,c) - allocate and memset

All original calls or auto_malloc have been updated to use the new
macros. In addition, auto_realloc(), auto_strdup(), auto_astring_alloc(),
and auto_bitmap_alloc() have been updated to take a machine parameter.

Changed validity check allocations to not rely on auto_alloc* anymore
because they are not done in the context of a machine.

One final change that is included is the removal of SMH_BANKn macros.
Just use SMH_BANK(n) instead, which is what the previous macros mapped
to anyhow.
2009-04-26 23:54:37 +00:00

543 lines
14 KiB
C

#include "driver.h"
#include "streams.h"
#include "samples.h"
typedef struct _sample_channel sample_channel;
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;
};
typedef struct _samples_info samples_info;
struct _samples_info
{
const device_config *device;
int numchannels; /* how many channels */
sample_channel *channel;/* array of channels */
loaded_samples *samples;/* array of samples */
};
INLINE samples_info *get_safe_token(const device_config *device)
{
assert(device != NULL);
assert(device->token != NULL);
assert(device->type == SOUND);
assert(sound_get_type(device) == SOUND_SAMPLES);
return (samples_info *)device->token;
}
#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
-------------------------------------------------*/
static int read_wav_sample(running_machine *machine, mame_file *f, loaded_sample *sample)
{
unsigned long offset = 0;
UINT32 length, rate, filesize;
UINT16 bits, temp16;
char buf[32];
UINT32 sindex;
/* 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 = LITTLE_ENDIANIZE_INT32(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 = LITTLE_ENDIANIZE_INT32(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 = LITTLE_ENDIANIZE_INT32(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 = LITTLE_ENDIANIZE_INT32(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_alloc_array(machine, INT16, 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_alloc_array(machine, INT16, length/2);
mame_fread(f, sample->data, length);
sample->length /= 2;
if (ENDIANNESS_NATIVE != ENDIANNESS_LITTLE)
for (sindex = 0; sindex < sample->length; sindex++)
sample->data[sindex] = LITTLE_ENDIANIZE_INT16(sample->data[sindex]);
}
return 1;
}
/*-------------------------------------------------
readsamples - load all samples
-------------------------------------------------*/
loaded_samples *readsamples(running_machine *machine, const char *const *samplenames, const char *basename)
{
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 = (loaded_samples *)auto_alloc_array_clear(machine, UINT8, sizeof(loaded_samples) + (i-1) * sizeof(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(machine, 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(const device_config *device,int channel,int samplenum,int loop)
{
samples_info *info = get_safe_token(device);
sample_channel *chan;
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_raw(const device_config *device,int channel,const INT16 *sampledata,int samples,int frequency,int loop)
{
samples_info *info = get_safe_token(device);
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_set_freq(const device_config *device,int channel,int freq)
{
samples_info *info = get_safe_token(device);
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_volume(const device_config *device,int channel,float volume)
{
samples_info *info = get_safe_token(device);
sample_channel *chan;
assert( channel < info->numchannels );
chan = &info->channel[channel];
stream_set_output_gain(chan->stream, 0, volume);
}
void sample_set_pause(const device_config *device,int channel,int pause)
{
samples_info *info = get_safe_token(device);
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_stop(const device_config *device,int channel)
{
samples_info *info = get_safe_token(device);
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;
}
int sample_get_base_freq(const device_config *device,int channel)
{
samples_info *info = get_safe_token(device);
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_playing(const device_config *device,int channel)
{
samples_info *info = get_safe_token(device);
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);
}
static STREAM_UPDATE( sample_update_sound )
{
sample_channel *chan = (sample_channel *)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 )
{
samples_info *info = (samples_info *)param;
int i;
/* loop over channels */
for (i = 0; i < info->numchannels; i++)
{
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)
{
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 DEVICE_START( samples )
{
int i;
const samples_interface *intf = (const samples_interface *)device->static_config;
samples_info *info = get_safe_token(device);
info->device = device;
/* read audio samples */
if (intf->samplenames)
info->samples = readsamples(device->machine, intf->samplenames,device->machine->gamedrv->name);
/* allocate channels */
info->numchannels = intf->channels;
assert(info->numchannels < MAX_CHANNELS);
info->channel = auto_alloc_array(device->machine, sample_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);
}
/**************************************************************************
* Generic get_info
**************************************************************************/
DEVICE_GET_INFO( samples )
{
switch (state)
{
/* --- the following bits of info are returned as 64-bit signed integers --- */
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(samples_info); break;
/* --- the following bits of info are returned as pointers to data or functions --- */
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( samples ); break;
case DEVINFO_FCT_STOP: /* Nothing */ break;
case DEVINFO_FCT_RESET: /* Nothing */ break;
/* --- the following bits of info are returned as NULL-terminated strings --- */
case DEVINFO_STR_NAME: strcpy(info->s, "Samples"); break;
case DEVINFO_STR_FAMILY: strcpy(info->s, "Big Hack"); break;
case DEVINFO_STR_VERSION: strcpy(info->s, "1.1"); break;
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
}
}