summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/sound/samples.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/sound/samples.c')
-rw-r--r--trunk/src/emu/sound/samples.c530
1 files changed, 530 insertions, 0 deletions
diff --git a/trunk/src/emu/sound/samples.c b/trunk/src/emu/sound/samples.c
new file mode 100644
index 00000000000..4e3e08c1a56
--- /dev/null
+++ b/trunk/src/emu/sound/samples.c
@@ -0,0 +1,530 @@
+#include "emu.h"
+#include "emuopts.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
+{
+ device_t *device;
+ int numchannels; /* how many channels */
+ sample_channel *channel;/* array of channels */
+ loaded_samples *samples;/* array of samples */
+};
+
+
+INLINE samples_info *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == SAMPLES);
+ return (samples_info *)downcast<legacy_device_base *>(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, emu_file &file, 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 += file.read(buf, 4);
+ if (offset < 4)
+ return 0;
+ if (memcmp(&buf[0], "RIFF", 4) != 0)
+ return 0;
+
+ /* get the total size */
+ offset += file.read(&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 += file.read(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 += file.read(buf, 4);
+ offset += file.read(&length, 4);
+ length = LITTLE_ENDIANIZE_INT32(length);
+ if (memcmp(&buf[0], "fmt ", 4) == 0)
+ break;
+
+ /* seek to the next block */
+ file.seek(length, SEEK_CUR);
+ offset += length;
+ if (offset >= filesize)
+ return 0;
+ }
+
+ /* read the format -- make sure it is PCM */
+ offset += file.read(&temp16, 2);
+ temp16 = LITTLE_ENDIANIZE_INT16(temp16);
+ if (temp16 != 1)
+ return 0;
+
+ /* number of channels -- only mono is supported */
+ offset += file.read(&temp16, 2);
+ temp16 = LITTLE_ENDIANIZE_INT16(temp16);
+ if (temp16 != 1)
+ return 0;
+
+ /* sample rate */
+ offset += file.read(&rate, 4);
+ rate = LITTLE_ENDIANIZE_INT32(rate);
+
+ /* bytes/second and block alignment are ignored */
+ offset += file.read(buf, 6);
+
+ /* bits/sample */
+ offset += file.read(&bits, 2);
+ bits = LITTLE_ENDIANIZE_INT16(bits);
+ if (bits != 8 && bits != 16)
+ return 0;
+
+ /* seek past any extra data */
+ file.seek(length - 16, SEEK_CUR);
+ offset += length - 16;
+
+ /* seek until we find a data tag */
+ while (1)
+ {
+ offset += file.read(buf, 4);
+ offset += file.read(&length, 4);
+ length = LITTLE_ENDIANIZE_INT32(length);
+ if (memcmp(&buf[0], "data", 4) == 0)
+ break;
+
+ /* seek to the next block */
+ file.seek(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);
+ file.read(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);
+ file.read(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 (!machine.options().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])
+ {
+ emu_file file(machine.options().sample_path(), OPEN_FLAG_READ);
+
+ file_error filerr = file.open(basename, PATH_SEPARATOR, samplenames[i+skipfirst]);
+ if (filerr != FILERR_NONE && skipfirst)
+ filerr = file.open(samplenames[0] + 1, PATH_SEPARATOR, samplenames[i+skipfirst]);
+
+ if (filerr == FILERR_NONE)
+ read_wav_sample(machine, file, &samples->sample[i]);
+ }
+
+ 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(device_t *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 */
+ chan->stream->update();
+
+ /* 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(device_t *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 */
+ chan->stream->update();
+
+ /* 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(device_t *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 */
+ chan->stream->update();
+
+ chan->step = ((INT64)freq << FRAC_BITS) / info->device->machine().sample_rate();
+}
+
+
+void sample_set_volume(device_t *device,int channel,float volume)
+{
+ samples_info *info = get_safe_token(device);
+ sample_channel *chan;
+
+ assert( channel < info->numchannels );
+
+ chan = &info->channel[channel];
+
+ chan->stream->set_output_gain(0, volume);
+}
+
+
+void sample_set_pause(device_t *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 */
+ chan->stream->update();
+
+ chan->paused = pause;
+}
+
+
+void sample_stop(device_t *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 */
+ chan->stream->update();
+ chan->source = NULL;
+ chan->source_num = -1;
+}
+
+
+int sample_get_base_freq(device_t *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 */
+ chan->stream->update();
+ return chan->basefreq;
+}
+
+
+int sample_playing(device_t *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 */
+ chan->stream->update();
+ 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 void samples_postload(samples_info *info)
+{
+ 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().system().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 = device->machine().sound().stream_alloc(*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 */
+ device->save_item(NAME(info->channel[i].source_length), i);
+ device->save_item(NAME(info->channel[i].source_num), i);
+ device->save_item(NAME(info->channel[i].pos), i);
+ device->save_item(NAME(info->channel[i].frac), i);
+ device->save_item(NAME(info->channel[i].step), i);
+ device->save_item(NAME(info->channel[i].loop), i);
+ device->save_item(NAME(info->channel[i].paused), i);
+ }
+ device->machine().save().register_postload(save_prepost_delegate(FUNC(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;
+ }
+}
+
+
+DEFINE_LEGACY_SOUND_DEVICE(SAMPLES, samples);