#include "driver.h" #include "streams.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 { const device_config *device; 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 -------------------------------------------------*/ 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]; 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_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; 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 -------------------------------------------------*/ 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) / info->device->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) / info->device->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) / info->device->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 STREAM_UPDATE( sample_update_sound ) { struct sample_channel *chan = 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 ) { 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 = device->static_config; struct samples_info *info; info = auto_malloc(sizeof(*info)); memset(info, 0, sizeof(*info)); info->device = device; 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(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); 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: strcpy(info->s, "Samples"); break; case SNDINFO_STR_CORE_FAMILY: strcpy(info->s, "Big Hack"); break; case SNDINFO_STR_CORE_VERSION: strcpy(info->s, "1.1"); break; case SNDINFO_STR_CORE_FILE: strcpy(info->s, __FILE__); break; case SNDINFO_STR_CORE_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break; } }