summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/samples.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/samples.c')
-rw-r--r--src/emu/sound/samples.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/src/emu/sound/samples.c b/src/emu/sound/samples.c
new file mode 100644
index 00000000000..8d773916e10
--- /dev/null
+++ b/src/emu/sound/samples.c
@@ -0,0 +1,612 @@
+#include "driver.h"
+#include "streams.h"
+#include "samples.h"
+
+
+struct sample_channel
+{
+ sound_stream *stream;
+ 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 **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,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,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;
+ 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 void samples_postload(void *param)
+{
+ 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 void *samples_start(int sndindex, int clock, const void *config)
+{
+ int i;
+ const struct Samplesinterface *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,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, 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_item("samples", sndindex * MAX_CHANNELS + i, info->channel[i].source_length);
+ state_save_register_item("samples", sndindex * MAX_CHANNELS + i, info->channel[i].source_num);
+ state_save_register_item("samples", sndindex * MAX_CHANNELS + i, info->channel[i].pos);
+ state_save_register_item("samples", sndindex * MAX_CHANNELS + i, info->channel[i].frac);
+ state_save_register_item("samples", sndindex * MAX_CHANNELS + i, info->channel[i].step);
+ state_save_register_item("samples", sndindex * MAX_CHANNELS + i, info->channel[i].loop);
+ state_save_register_item("samples", sndindex * MAX_CHANNELS + i, info->channel[i].paused);
+ }
+ state_save_register_func_postload_ptr(samples_postload, info);
+
+ /* initialize any custom handlers */
+ if (intf->start)
+ (*intf->start)();
+
+ return info;
+}
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+static void samples_set_info(void *token, UINT32 state, sndinfo *info)
+{
+ switch (state)
+ {
+ /* no parameters to set */
+ }
+}
+
+
+void samples_get_info(void *token, UINT32 state, sndinfo *info)
+{
+ 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 = samples_set_info; break;
+ case SNDINFO_PTR_START: info->start = samples_start; 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 (c) 2007, The MAME Team"; break;
+ }
+}
+