mirror of
https://github.com/holub/mame
synced 2025-06-02 19:06:43 +03:00
1044 lines
30 KiB
C
1044 lines
30 KiB
C
/***************************************************************************
|
|
|
|
sound.c
|
|
|
|
Core sound functions and definitions.
|
|
|
|
Copyright Nicola Salmoria and the MAME Team.
|
|
Visit http://mamedev.org for licensing and usage restrictions.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "driver.h"
|
|
#include "osdepend.h"
|
|
#include "streams.h"
|
|
#include "config.h"
|
|
#include "profiler.h"
|
|
#include "sound/wavwrite.h"
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
DEBUGGING
|
|
***************************************************************************/
|
|
|
|
#define VERBOSE (0)
|
|
|
|
#define VPRINTF(x) do { if (VERBOSE) mame_printf_debug x; } while (0)
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
#define MAX_MIXER_CHANNELS 100
|
|
#define SOUND_UPDATE_FREQUENCY ATTOTIME_IN_HZ(50)
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
TYPE DEFINITIONS
|
|
***************************************************************************/
|
|
|
|
typedef struct _sound_output sound_output;
|
|
struct _sound_output
|
|
{
|
|
sound_stream * stream; /* associated stream */
|
|
int output; /* output number */
|
|
};
|
|
|
|
|
|
typedef struct _sound_info sound_info;
|
|
struct _sound_info
|
|
{
|
|
const sound_config *sound; /* pointer to the sound info */
|
|
int outputs; /* number of outputs from this instance */
|
|
sound_output * output; /* array of output information */
|
|
};
|
|
|
|
|
|
typedef struct _speaker_input speaker_input;
|
|
struct _speaker_input
|
|
{
|
|
float gain; /* current gain */
|
|
float default_gain; /* default gain */
|
|
char * name; /* name of this input */
|
|
};
|
|
|
|
|
|
typedef struct _speaker_info speaker_info;
|
|
struct _speaker_info
|
|
{
|
|
const speaker_config *speaker; /* pointer to the speaker info */
|
|
const char * tag; /* speaker tag */
|
|
sound_stream * mixer_stream; /* mixing stream */
|
|
int inputs; /* number of input streams */
|
|
speaker_input * input; /* array of input information */
|
|
#ifdef MAME_DEBUG
|
|
INT32 max_sample; /* largest sample value we've seen */
|
|
INT32 clipped_samples; /* total number of clipped samples */
|
|
INT32 total_samples; /* total number of samples */
|
|
#endif
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
GLOBAL VARIABLES
|
|
***************************************************************************/
|
|
|
|
static emu_timer *sound_update_timer;
|
|
|
|
static int totalsnd;
|
|
static sound_info sound[MAX_SOUND];
|
|
|
|
static INT16 *finalmix;
|
|
static UINT32 finalmix_leftover;
|
|
static INT32 *leftmix, *rightmix;
|
|
|
|
static int sound_muted;
|
|
static int sound_attenuation;
|
|
static int global_sound_enabled;
|
|
static int nosound_mode;
|
|
|
|
static wav_file *wavfile;
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
FUNCTION PROTOTYPES
|
|
***************************************************************************/
|
|
|
|
static void sound_reset(running_machine *machine);
|
|
static void sound_exit(running_machine *machine);
|
|
static void sound_pause(running_machine *machine, int pause);
|
|
static void sound_load(running_machine *machine, int config_type, xml_data_node *parentnode);
|
|
static void sound_save(running_machine *machine, int config_type, xml_data_node *parentnode);
|
|
static TIMER_CALLBACK( sound_update );
|
|
static void start_sound_chips(running_machine *machine);
|
|
static void route_sound(running_machine *machine);
|
|
static void mixer_update(void *param, stream_sample_t **inputs, stream_sample_t **buffer, int length);
|
|
static STATE_POSTLOAD( mixer_postload );
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
INLINE FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
get_safe_token - makes sure that the passed
|
|
in device is, in fact, a timer
|
|
-------------------------------------------------*/
|
|
|
|
INLINE speaker_info *get_safe_token(const device_config *device)
|
|
{
|
|
assert(device != NULL);
|
|
assert(device->token != NULL);
|
|
assert(device->type == SPEAKER_OUTPUT);
|
|
|
|
return (speaker_info *)device->token;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
find_speaker_by_tag - find a tagged speaker
|
|
-------------------------------------------------*/
|
|
|
|
INLINE speaker_info *find_speaker_by_tag(running_machine *machine, const char *tag)
|
|
{
|
|
const device_config *speaker = device_list_find_by_tag(machine->config->devicelist, SPEAKER_OUTPUT, tag);
|
|
return (speaker == NULL) ? NULL : speaker->token;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
find_sound_by_tag - find a tagged sound chip
|
|
-------------------------------------------------*/
|
|
|
|
INLINE sound_info *find_sound_by_tag(const char *tag)
|
|
{
|
|
int sndnum;
|
|
|
|
/* attempt to find the speaker in our list */
|
|
for (sndnum = 0; sndnum < totalsnd; sndnum++)
|
|
if (sound[sndnum].sound->tag && !strcmp(sound[sndnum].sound->tag, tag))
|
|
return &sound[sndnum];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
INITIALIZATION
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
sound_init - start up the sound system
|
|
-------------------------------------------------*/
|
|
|
|
void sound_init(running_machine *machine)
|
|
{
|
|
attotime update_frequency = SOUND_UPDATE_FREQUENCY;
|
|
const char *filename;
|
|
|
|
/* handle -nosound */
|
|
nosound_mode = !options_get_bool(mame_options(), OPTION_SOUND);
|
|
if (nosound_mode)
|
|
machine->sample_rate = 11025;
|
|
|
|
/* count the speakers */
|
|
VPRINTF(("total speakers = %d\n", speaker_output_count(machine->config)));
|
|
|
|
/* allocate memory for mix buffers */
|
|
leftmix = auto_malloc(machine->sample_rate * sizeof(*leftmix));
|
|
rightmix = auto_malloc(machine->sample_rate * sizeof(*rightmix));
|
|
finalmix = auto_malloc(machine->sample_rate * sizeof(*finalmix));
|
|
|
|
/* allocate a global timer for sound timing */
|
|
sound_update_timer = timer_alloc(sound_update, NULL);
|
|
timer_adjust_periodic(sound_update_timer, update_frequency, 0, update_frequency);
|
|
|
|
/* initialize the streams engine */
|
|
VPRINTF(("streams_init\n"));
|
|
streams_init(machine, update_frequency.attoseconds);
|
|
|
|
/* now start up the sound chips and tag their streams */
|
|
VPRINTF(("start_sound_chips\n"));
|
|
start_sound_chips(machine);
|
|
|
|
/* finally, do all the routing */
|
|
VPRINTF(("route_sound\n"));
|
|
route_sound(machine);
|
|
|
|
/* open the output WAV file if specified */
|
|
filename = options_get_string(mame_options(), OPTION_WAVWRITE);
|
|
if (filename[0] != 0)
|
|
wavfile = wav_open(filename, machine->sample_rate, 2);
|
|
|
|
/* enable sound by default */
|
|
global_sound_enabled = TRUE;
|
|
sound_muted = FALSE;
|
|
sound_set_attenuation(options_get_int(mame_options(), OPTION_VOLUME));
|
|
|
|
/* register callbacks */
|
|
config_register(machine, "mixer", sound_load, sound_save);
|
|
add_pause_callback(machine, sound_pause);
|
|
add_reset_callback(machine, sound_reset);
|
|
add_exit_callback(machine, sound_exit);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_exit - clean up after ourselves
|
|
-------------------------------------------------*/
|
|
|
|
static void sound_exit(running_machine *machine)
|
|
{
|
|
int sndnum;
|
|
|
|
/* close any open WAV file */
|
|
if (wavfile != NULL)
|
|
wav_close(wavfile);
|
|
|
|
/* stop all the sound chips */
|
|
for (sndnum = 0; sndnum < MAX_SOUND; sndnum++)
|
|
if (machine->config->sound[sndnum].type != SOUND_DUMMY)
|
|
sndintrf_exit_sound(sndnum);
|
|
|
|
/* reset variables */
|
|
totalsnd = 0;
|
|
memset(sound, 0, sizeof(sound));
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
INITIALIZATION HELPERS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
start_sound_chips - loop over all sound chips
|
|
and initialize them
|
|
-------------------------------------------------*/
|
|
|
|
static void start_sound_chips(running_machine *machine)
|
|
{
|
|
int sndnum;
|
|
|
|
/* reset the sound array */
|
|
memset(sound, 0, sizeof(sound));
|
|
|
|
/* start up all the sound chips */
|
|
for (sndnum = 0; sndnum < MAX_SOUND; sndnum++)
|
|
{
|
|
const sound_config *msound = &machine->config->sound[sndnum];
|
|
sound_info *info;
|
|
int num_regs;
|
|
int index;
|
|
|
|
/* stop when we hit an empty entry */
|
|
if (msound->type == SOUND_DUMMY)
|
|
break;
|
|
totalsnd++;
|
|
|
|
/* zap all the info */
|
|
info = &sound[sndnum];
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
/* copy in all the relevant info */
|
|
info->sound = msound;
|
|
|
|
/* start the chip, tagging all its streams */
|
|
VPRINTF(("sndnum = %d -- sound_type = %d\n", sndnum, msound->type));
|
|
num_regs = state_save_get_reg_count();
|
|
streams_set_tag(machine, info);
|
|
if (sndintrf_init_sound(sndnum, msound->tag, msound->type, msound->clock, msound->config) != 0)
|
|
fatalerror("Sound chip #%d (%s) failed to initialize!", sndnum, sndnum_name(sndnum));
|
|
|
|
/* if no state registered for saving, we can't save */
|
|
num_regs = state_save_get_reg_count() - num_regs;
|
|
if (num_regs == 0)
|
|
{
|
|
logerror("Sound chip #%d (%s) did not register any state to save!\n", sndnum, sndnum_name(sndnum));
|
|
if (machine->gamedrv->flags & GAME_SUPPORTS_SAVE)
|
|
fatalerror("Sound chip #%d (%s) did not register any state to save!", sndnum, sndnum_name(sndnum));
|
|
}
|
|
|
|
/* now count the outputs */
|
|
VPRINTF(("Counting outputs\n"));
|
|
for (index = 0; ; index++)
|
|
{
|
|
sound_stream *stream = stream_find_by_tag(machine, info, index);
|
|
if (!stream)
|
|
break;
|
|
info->outputs += stream_get_outputs(stream);
|
|
VPRINTF((" stream %p, %d outputs\n", stream, stream_get_outputs(stream)));
|
|
}
|
|
|
|
/* if we have outputs, examine them */
|
|
if (info->outputs)
|
|
{
|
|
/* allocate an array to hold them */
|
|
info->output = auto_malloc(info->outputs * sizeof(*info->output));
|
|
VPRINTF((" %d outputs total\n", info->outputs));
|
|
|
|
/* now fill the array */
|
|
info->outputs = 0;
|
|
for (index = 0; ; index++)
|
|
{
|
|
sound_stream *stream = stream_find_by_tag(machine, info, index);
|
|
int outputs, outputnum;
|
|
|
|
if (!stream)
|
|
break;
|
|
outputs = stream_get_outputs(stream);
|
|
|
|
/* fill in an entry for each output */
|
|
for (outputnum = 0; outputnum < outputs; outputnum++)
|
|
{
|
|
info->output[info->outputs].stream = stream;
|
|
info->output[info->outputs].output = outputnum;
|
|
info->outputs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
route_sound - route sound outputs to target
|
|
inputs
|
|
-------------------------------------------------*/
|
|
|
|
static void route_sound(running_machine *machine)
|
|
{
|
|
int sndnum, routenum, outputnum;
|
|
const device_config *curspeak;
|
|
|
|
/* iterate over all the sound chips */
|
|
for (sndnum = 0; sndnum < totalsnd; sndnum++)
|
|
{
|
|
sound_info *info = &sound[sndnum];
|
|
|
|
/* iterate over all routes */
|
|
for (routenum = 0; routenum < info->sound->routes; routenum++)
|
|
{
|
|
const sound_route *mroute = &info->sound->route[routenum];
|
|
speaker_info *speaker;
|
|
sound_info *sound;
|
|
|
|
/* find the target */
|
|
speaker = find_speaker_by_tag(machine, mroute->target);
|
|
sound = find_sound_by_tag(mroute->target);
|
|
|
|
/* if neither found, it's fatal */
|
|
if (speaker == NULL && sound == NULL)
|
|
fatalerror("Sound route \"%s\" not found!\n", mroute->target);
|
|
|
|
/* if we got a speaker, bump its input count */
|
|
if (speaker != NULL)
|
|
{
|
|
if (mroute->output >= 0 && mroute->output < info->outputs)
|
|
speaker->inputs++;
|
|
else if (mroute->output == ALL_OUTPUTS)
|
|
speaker->inputs += info->outputs;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now allocate the mixers and input data */
|
|
streams_set_tag(machine, NULL);
|
|
for (curspeak = speaker_output_first(machine->config); curspeak != NULL; curspeak = speaker_output_next(curspeak))
|
|
{
|
|
speaker_info *info = curspeak->token;
|
|
if (info->inputs != 0)
|
|
{
|
|
info->mixer_stream = stream_create(info->inputs, 1, machine->sample_rate, info, mixer_update);
|
|
state_save_register_postload(machine, mixer_postload, info->mixer_stream);
|
|
info->input = auto_malloc(info->inputs * sizeof(*info->input));
|
|
info->inputs = 0;
|
|
}
|
|
else
|
|
logerror("Warning: speaker \"%s\" has no inputs\n", info->tag);
|
|
}
|
|
|
|
/* iterate again over all the sound chips */
|
|
for (sndnum = 0; sndnum < totalsnd; sndnum++)
|
|
{
|
|
sound_info *info = &sound[sndnum];
|
|
|
|
/* iterate over all routes */
|
|
for (routenum = 0; routenum < info->sound->routes; routenum++)
|
|
{
|
|
const sound_route *mroute = &info->sound->route[routenum];
|
|
speaker_info *speaker;
|
|
sound_info *sound;
|
|
|
|
/* find the target */
|
|
speaker = find_speaker_by_tag(machine, mroute->target);
|
|
sound = find_sound_by_tag(mroute->target);
|
|
|
|
/* if it's a speaker, set the input */
|
|
if (speaker != NULL)
|
|
{
|
|
for (outputnum = 0; outputnum < info->outputs; outputnum++)
|
|
if (mroute->output == outputnum || mroute->output == ALL_OUTPUTS)
|
|
{
|
|
char namebuf[256];
|
|
int index;
|
|
|
|
sound_type sndtype = sndnum_to_sndti(sndnum, &index);
|
|
|
|
/* built the display name */
|
|
namebuf[0] = '\0';
|
|
|
|
/* speaker name, if more than one speaker */
|
|
if (speaker_output_count(machine->config) > 1)
|
|
sprintf(namebuf, "%sSpeaker '%s': ", namebuf, speaker->tag);
|
|
|
|
/* device name */
|
|
sprintf(namebuf, "%s%s ", namebuf, sndnum_name(sndnum));
|
|
|
|
/* device index, if more than one of this type */
|
|
if (sndtype_count(sndtype) > 1)
|
|
sprintf(namebuf, "%s#%d ", namebuf, index);
|
|
|
|
/* channel number, if more than channel for this device */
|
|
if (info->outputs > 1)
|
|
sprintf(namebuf, "%sCh.%d", namebuf, outputnum);
|
|
|
|
/* remove final space */
|
|
if (namebuf[strlen(namebuf) - 1] == ' ')
|
|
namebuf[strlen(namebuf) - 1] = '\0';
|
|
|
|
/* fill in the input data on this speaker */
|
|
speaker->input[speaker->inputs].gain = mroute->gain;
|
|
speaker->input[speaker->inputs].default_gain = mroute->gain;
|
|
speaker->input[speaker->inputs].name = auto_strdup(namebuf);
|
|
|
|
/* connect the output to the input */
|
|
stream_set_input(speaker->mixer_stream, speaker->inputs++, info->output[outputnum].stream, info->output[outputnum].output, mroute->gain);
|
|
}
|
|
}
|
|
|
|
/* if it's a sound chip, set the input */
|
|
else
|
|
{
|
|
if (mroute->input < 0)
|
|
{
|
|
for (outputnum = 0; outputnum < info->outputs; outputnum++)
|
|
if (mroute->output == outputnum || mroute->output == ALL_OUTPUTS)
|
|
stream_set_input(sound->output[0].stream, 0, info->output[outputnum].stream, info->output[outputnum].output, mroute->gain);
|
|
}
|
|
else
|
|
{
|
|
assert(mroute->output != ALL_OUTPUTS);
|
|
for (outputnum = 0; outputnum < info->outputs; outputnum++)
|
|
if (mroute->output == outputnum)
|
|
stream_set_input(sound->output[0].stream, mroute->input, info->output[outputnum].stream, info->output[outputnum].output, mroute->gain);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
GLOBAL STATE MANAGEMENT
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
sound_reset - reset all sound chips
|
|
-------------------------------------------------*/
|
|
|
|
static void sound_reset(running_machine *machine)
|
|
{
|
|
int sndnum;
|
|
|
|
/* reset all the sound chips */
|
|
for (sndnum = 0; sndnum < MAX_SOUND; sndnum++)
|
|
if (machine->config->sound[sndnum].type != SOUND_DUMMY)
|
|
sndnum_reset(sndnum);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_pause - pause sound output
|
|
-------------------------------------------------*/
|
|
|
|
static void sound_pause(running_machine *machine, int pause)
|
|
{
|
|
if (pause)
|
|
sound_muted |= 0x02;
|
|
else
|
|
sound_muted &= ~0x02;
|
|
osd_set_mastervolume(sound_muted ? -32 : sound_attenuation);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_pause - pause sound output
|
|
-------------------------------------------------*/
|
|
|
|
void sound_mute(int mute)
|
|
{
|
|
if (mute)
|
|
sound_muted |= 0x01;
|
|
else
|
|
sound_muted &= ~0x01;
|
|
osd_set_mastervolume(sound_muted ? -32 : sound_attenuation);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_set_attenuation - set the global volume
|
|
-------------------------------------------------*/
|
|
|
|
void sound_set_attenuation(int attenuation)
|
|
{
|
|
sound_attenuation = attenuation;
|
|
osd_set_mastervolume(sound_muted ? -32 : sound_attenuation);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_get_attenuation - return the global
|
|
volume
|
|
-------------------------------------------------*/
|
|
|
|
int sound_get_attenuation(void)
|
|
{
|
|
return sound_attenuation;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_global_enable - enable/disable sound
|
|
globally
|
|
-------------------------------------------------*/
|
|
|
|
void sound_global_enable(int enable)
|
|
{
|
|
global_sound_enabled = enable;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
SOUND SAVE/LOAD
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
sound_load - read and apply data from the
|
|
configuration file
|
|
-------------------------------------------------*/
|
|
|
|
static void sound_load(running_machine *machine, int config_type, xml_data_node *parentnode)
|
|
{
|
|
xml_data_node *channelnode;
|
|
int mixernum;
|
|
|
|
/* we only care about game files */
|
|
if (config_type != CONFIG_TYPE_GAME)
|
|
return;
|
|
|
|
/* might not have any data */
|
|
if (parentnode == NULL)
|
|
return;
|
|
|
|
/* iterate over channel nodes */
|
|
for (channelnode = xml_get_sibling(parentnode->child, "channel"); channelnode; channelnode = xml_get_sibling(channelnode->next, "channel"))
|
|
{
|
|
mixernum = xml_get_attribute_int(channelnode, "index", -1);
|
|
if (mixernum >= 0 && mixernum < MAX_MIXER_CHANNELS)
|
|
{
|
|
float defvol = xml_get_attribute_float(channelnode, "defvol", -1000.0);
|
|
float newvol = xml_get_attribute_float(channelnode, "newvol", -1000.0);
|
|
if (fabs(defvol - sound_get_default_gain(machine, mixernum)) < 1e-6 && newvol != -1000.0)
|
|
sound_set_user_gain(machine, mixernum, newvol);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_save - save data to the configuration
|
|
file
|
|
-------------------------------------------------*/
|
|
|
|
static void sound_save(running_machine *machine, int config_type, xml_data_node *parentnode)
|
|
{
|
|
int mixernum;
|
|
|
|
/* we only care about game files */
|
|
if (config_type != CONFIG_TYPE_GAME)
|
|
return;
|
|
|
|
/* iterate over mixer channels */
|
|
if (parentnode != NULL)
|
|
for (mixernum = 0; mixernum < MAX_MIXER_CHANNELS; mixernum++)
|
|
{
|
|
float defvol = sound_get_default_gain(machine, mixernum);
|
|
float newvol = sound_get_user_gain(machine, mixernum);
|
|
|
|
if (defvol != newvol)
|
|
{
|
|
xml_data_node *channelnode = xml_add_child(parentnode, "channel", NULL);
|
|
if (channelnode != NULL)
|
|
{
|
|
xml_set_attribute_int(channelnode, "index", mixernum);
|
|
xml_set_attribute_float(channelnode, "defvol", defvol);
|
|
xml_set_attribute_float(channelnode, "newvol", newvol);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
MIXING STAGE
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
sound_update - mix everything down to
|
|
its final form and send it to the OSD layer
|
|
-------------------------------------------------*/
|
|
|
|
static TIMER_CALLBACK( sound_update )
|
|
{
|
|
UINT32 finalmix_step, finalmix_offset;
|
|
const device_config *curspeak;
|
|
int samples_this_update = 0;
|
|
int sample;
|
|
|
|
VPRINTF(("sound_update\n"));
|
|
|
|
profiler_mark(PROFILER_SOUND);
|
|
|
|
/* force all the speaker streams to generate the proper number of samples */
|
|
for (curspeak = speaker_output_first(machine->config); curspeak != NULL; curspeak = speaker_output_next(curspeak))
|
|
{
|
|
speaker_info *spk = curspeak->token;
|
|
const stream_sample_t *stream_buf;
|
|
|
|
/* get the output buffer */
|
|
if (spk->mixer_stream != NULL)
|
|
{
|
|
int numsamples;
|
|
|
|
/* update the stream, getting the start/end pointers around the operation */
|
|
stream_buf = stream_get_output_since_last_update(spk->mixer_stream, 0, &numsamples);
|
|
|
|
/* set or assert that all streams have the same count */
|
|
if (samples_this_update == 0)
|
|
{
|
|
samples_this_update = numsamples;
|
|
|
|
/* reset the mixing streams */
|
|
memset(leftmix, 0, samples_this_update * sizeof(*leftmix));
|
|
memset(rightmix, 0, samples_this_update * sizeof(*rightmix));
|
|
}
|
|
assert(samples_this_update == numsamples);
|
|
|
|
#ifdef MAME_DEBUG
|
|
/* debug version: keep track of the maximum sample */
|
|
for (sample = 0; sample < samples_this_update; sample++)
|
|
{
|
|
if (stream_buf[sample] > spk->max_sample)
|
|
spk->max_sample = stream_buf[sample];
|
|
else if (-stream_buf[sample] > spk->max_sample)
|
|
spk->max_sample = -stream_buf[sample];
|
|
if (stream_buf[sample] > 32767 || stream_buf[sample] < -32768)
|
|
spk->clipped_samples++;
|
|
spk->total_samples++;
|
|
}
|
|
#endif
|
|
|
|
/* mix if sound is enabled */
|
|
if (global_sound_enabled && !nosound_mode)
|
|
{
|
|
/* if the speaker is centered, send to both left and right */
|
|
if (spk->speaker->x == 0)
|
|
for (sample = 0; sample < samples_this_update; sample++)
|
|
{
|
|
leftmix[sample] += stream_buf[sample];
|
|
rightmix[sample] += stream_buf[sample];
|
|
}
|
|
|
|
/* if the speaker is to the left, send only to the left */
|
|
else if (spk->speaker->x < 0)
|
|
for (sample = 0; sample < samples_this_update; sample++)
|
|
leftmix[sample] += stream_buf[sample];
|
|
|
|
/* if the speaker is to the right, send only to the right */
|
|
else
|
|
for (sample = 0; sample < samples_this_update; sample++)
|
|
rightmix[sample] += stream_buf[sample];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* now downmix the final result */
|
|
finalmix_step = video_get_speed_factor();
|
|
finalmix_offset = 0;
|
|
for (sample = finalmix_leftover; sample < samples_this_update * 100; sample += finalmix_step)
|
|
{
|
|
int sampindex = sample / 100;
|
|
INT32 samp;
|
|
|
|
/* clamp the left side */
|
|
samp = leftmix[sampindex];
|
|
if (samp < -32768)
|
|
samp = -32768;
|
|
else if (samp > 32767)
|
|
samp = 32767;
|
|
finalmix[finalmix_offset++] = samp;
|
|
|
|
/* clamp the right side */
|
|
samp = rightmix[sampindex];
|
|
if (samp < -32768)
|
|
samp = -32768;
|
|
else if (samp > 32767)
|
|
samp = 32767;
|
|
finalmix[finalmix_offset++] = samp;
|
|
}
|
|
finalmix_leftover = sample - samples_this_update * 100;
|
|
|
|
/* play the result */
|
|
if (finalmix_offset > 0)
|
|
{
|
|
osd_update_audio_stream(machine, finalmix, finalmix_offset / 2);
|
|
video_avi_add_sound(machine, finalmix, finalmix_offset / 2);
|
|
if (wavfile != NULL)
|
|
wav_add_data_16(wavfile, finalmix, finalmix_offset);
|
|
}
|
|
|
|
/* update the streamer */
|
|
streams_update(machine);
|
|
|
|
profiler_mark(PROFILER_END);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
mixer_update - mix all inputs to one output
|
|
-------------------------------------------------*/
|
|
|
|
static void mixer_update(void *param, stream_sample_t **inputs, stream_sample_t **buffer, int length)
|
|
{
|
|
speaker_info *speaker = param;
|
|
int numinputs = speaker->inputs;
|
|
int pos;
|
|
|
|
VPRINTF(("Mixer_update(%d)\n", length));
|
|
|
|
/* loop over samples */
|
|
for (pos = 0; pos < length; pos++)
|
|
{
|
|
INT32 sample = inputs[0][pos];
|
|
int inp;
|
|
|
|
/* add up all the inputs */
|
|
for (inp = 1; inp < numinputs; inp++)
|
|
sample += inputs[inp][pos];
|
|
buffer[0][pos] = sample;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
mixer_postload - postload function to reset
|
|
the mixer stream to the proper sample rate
|
|
-------------------------------------------------*/
|
|
|
|
static STATE_POSTLOAD( mixer_postload )
|
|
{
|
|
sound_stream *stream = param;
|
|
stream_set_sample_rate(stream, machine->sample_rate);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
SPEAKER OUTPUT DEVICE INTERFACE
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
speaker_output_start - device start callback
|
|
for a speaker
|
|
-------------------------------------------------*/
|
|
|
|
static DEVICE_START( speaker_output )
|
|
{
|
|
speaker_info *info = device->token;
|
|
|
|
/* copy in all the relevant info */
|
|
info->speaker = device->inline_config;
|
|
info->tag = device->tag;
|
|
|
|
return DEVICE_START_OK;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
speaker_output_stop - device stop callback
|
|
for a speaker
|
|
-------------------------------------------------*/
|
|
|
|
static DEVICE_STOP( speaker_output )
|
|
{
|
|
#ifdef MAME_DEBUG
|
|
speaker_info *info = device->token;
|
|
|
|
/* log the maximum sample values for all speakers */
|
|
if (info->max_sample > 0)
|
|
mame_printf_debug("Speaker \"%s\" - max = %d (gain *= %f) - %d%% samples clipped\n", info->tag, info->max_sample, 32767.0 / (info->max_sample ? info->max_sample : 1), (int)((double)info->clipped_samples * 100.0 / info->total_samples));
|
|
#endif /* MAME_DEBUG */
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
speaker_output_set_info - device set info
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
static DEVICE_SET_INFO( speaker_output )
|
|
{
|
|
switch (state)
|
|
{
|
|
/* no parameters to set */
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
speaker_output_get_info - device get info
|
|
callback
|
|
-------------------------------------------------*/
|
|
|
|
DEVICE_GET_INFO( speaker_output )
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
|
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(speaker_info); break;
|
|
case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(speaker_config); break;
|
|
case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_AUDIO; break;
|
|
|
|
/* --- the following bits of info are returned as pointers to data or functions --- */
|
|
case DEVINFO_FCT_SET_INFO: info->set_info = DEVICE_SET_INFO_NAME(speaker_output); break;
|
|
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(speaker_output); break;
|
|
case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME(speaker_output); break;
|
|
case DEVINFO_FCT_RESET: /* Nothing */ break;
|
|
|
|
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
|
case DEVINFO_STR_NAME: info->s = "Speaker"; break;
|
|
case DEVINFO_STR_FAMILY: info->s = "Sound"; break;
|
|
case DEVINFO_STR_VERSION: info->s = "1.0"; break;
|
|
case DEVINFO_STR_SOURCE_FILE: info->s = __FILE__; break;
|
|
case DEVINFO_STR_CREDITS: info->s = "Copyright Nicola Salmoria and the MAME Team"; break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
MISCELLANEOUS HELPERS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
sndti_set_output_gain - set the gain of a
|
|
particular output
|
|
-------------------------------------------------*/
|
|
|
|
void sndti_set_output_gain(sound_type type, int index, int output, float gain)
|
|
{
|
|
int sndnum = sndti_to_sndnum(type, index);
|
|
|
|
if (sndnum < 0)
|
|
{
|
|
logerror("sndti_set_output_gain called for invalid sound type %d, index %d\n", type, index);
|
|
return;
|
|
}
|
|
if (output >= sound[sndnum].outputs)
|
|
{
|
|
logerror("sndti_set_output_gain called for invalid sound output %d (type %d, index %d)\n", output, type, index);
|
|
return;
|
|
}
|
|
stream_set_output_gain(sound[sndnum].output[output].stream, sound[sndnum].output[output].output, gain);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
USER GAIN CONTROLS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
index_to_input - map an absolute index to
|
|
a particular input
|
|
-------------------------------------------------*/
|
|
|
|
INLINE speaker_info *index_to_input(running_machine *machine, int index, int *input)
|
|
{
|
|
const device_config *curspeak;
|
|
int count = 0;
|
|
|
|
/* scan through the speakers until we find the indexed input */
|
|
for (curspeak = speaker_output_first(machine->config); curspeak != NULL; curspeak = speaker_output_next(curspeak))
|
|
{
|
|
speaker_info *info = curspeak->token;
|
|
if (index < count + info->inputs)
|
|
{
|
|
*input = index - count;
|
|
return info;
|
|
}
|
|
count += info->inputs;
|
|
}
|
|
|
|
/* index out of range */
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_get_user_gain_count - return the number
|
|
of user-controllable gain parameters
|
|
-------------------------------------------------*/
|
|
|
|
int sound_get_user_gain_count(running_machine *machine)
|
|
{
|
|
const device_config *curspeak;
|
|
int count = 0;
|
|
|
|
/* count up the number of speaker inputs */
|
|
for (curspeak = speaker_output_first(machine->config); curspeak != NULL; curspeak = speaker_output_next(curspeak))
|
|
{
|
|
speaker_info *info = curspeak->token;
|
|
count += info->inputs;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_set_user_gain - set the nth user gain
|
|
value
|
|
-------------------------------------------------*/
|
|
|
|
void sound_set_user_gain(running_machine *machine, int index, float gain)
|
|
{
|
|
int inputnum;
|
|
speaker_info *spk = index_to_input(machine, index, &inputnum);
|
|
|
|
if (spk != NULL)
|
|
{
|
|
spk->input[inputnum].gain = gain;
|
|
stream_set_input_gain(spk->mixer_stream, inputnum, gain);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_get_user_gain - get the nth user gain
|
|
value
|
|
-------------------------------------------------*/
|
|
|
|
float sound_get_user_gain(running_machine *machine, int index)
|
|
{
|
|
int inputnum;
|
|
speaker_info *spk = index_to_input(machine, index, &inputnum);
|
|
return (spk != NULL) ? spk->input[inputnum].gain : 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_get_default_gain - return the default
|
|
gain of the nth user value
|
|
-------------------------------------------------*/
|
|
|
|
float sound_get_default_gain(running_machine *machine, int index)
|
|
{
|
|
int inputnum;
|
|
speaker_info *spk = index_to_input(machine, index, &inputnum);
|
|
return (spk != NULL) ? spk->input[inputnum].default_gain : 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_get_user_gain_name - return the name
|
|
of the nth user value
|
|
-------------------------------------------------*/
|
|
|
|
const char *sound_get_user_gain_name(running_machine *machine, int index)
|
|
{
|
|
int inputnum;
|
|
speaker_info *spk = index_to_input(machine, index, &inputnum);
|
|
return (spk != NULL) ? spk->input[inputnum].name : NULL;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
sound_find_sndnum_by_tag - return a sndnum
|
|
by finding the appropriate tag
|
|
-------------------------------------------------*/
|
|
|
|
int sound_find_sndnum_by_tag(const char *tag)
|
|
{
|
|
int index;
|
|
|
|
/* find a match */
|
|
for (index = 0; index < MAX_SOUND; index++)
|
|
if (sound[index].sound != NULL && sound[index].sound->tag != NULL)
|
|
if (strcmp(tag, sound[index].sound->tag) == 0)
|
|
return index;
|
|
|
|
return -1;
|
|
}
|