diff options
Diffstat (limited to 'trunk/src/emu/disound.c')
-rw-r--r-- | trunk/src/emu/disound.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/trunk/src/emu/disound.c b/trunk/src/emu/disound.c new file mode 100644 index 00000000000..672842de5f0 --- /dev/null +++ b/trunk/src/emu/disound.c @@ -0,0 +1,500 @@ +/*************************************************************************** + + disound.c + + Device sound interfaces. + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include "emu.h" + + + +//************************************************************************** +// DEVICE CONFIG SOUND INTERFACE +//************************************************************************** + +//------------------------------------------------- +// device_sound_interface - constructor +//------------------------------------------------- + +device_sound_interface::device_sound_interface(const machine_config &mconfig, device_t &device) + : device_interface(device), + m_outputs(0), + m_auto_allocated_inputs(0) +{ +} + + +//------------------------------------------------- +// ~device_sound_interface - destructor +//------------------------------------------------- + +device_sound_interface::~device_sound_interface() +{ +} + + +//------------------------------------------------- +// static_add_route - configuration helper to add +// a new route to the device +//------------------------------------------------- + +device_sound_interface::sound_route &device_sound_interface::static_add_route(device_t &device, UINT32 output, const char *target, double gain, UINT32 input, UINT32 mixoutput) +{ + // find our sound interface + device_sound_interface *sound; + if (!device.interface(sound)) + throw emu_fatalerror("MCFG_SOUND_ROUTE called on device '%s' with no sound interface", device.tag()); + + // append a new route to the list + return sound->m_route_list.append(*global_alloc(sound_route(output, input, gain, target, mixoutput))); +} + + +//------------------------------------------------- +// static_reset_routes - configuration helper to +// reset all existing routes to the device +//------------------------------------------------- + +void device_sound_interface::static_reset_routes(device_t &device) +{ + // find our sound interface + device_sound_interface *sound; + if (!device.interface(sound)) + throw emu_fatalerror("MCFG_SOUND_ROUTES_RESET called on device '%s' with no sound interface", device.tag()); + + // reset the routine list + sound->m_route_list.reset(); +} + + +//------------------------------------------------- +// stream_alloc - allocate a stream implicitly +// associated with this device +//------------------------------------------------- + +sound_stream *device_sound_interface::stream_alloc(int inputs, int outputs, int sample_rate) +{ + return device().machine().sound().stream_alloc(*this, inputs, outputs, sample_rate); +} + + +//------------------------------------------------- +// inputs - return the total number of inputs +// for the given device +//------------------------------------------------- + +int device_sound_interface::inputs() const +{ + // scan the list counting streams we own and summing their inputs + int inputs = 0; + for (sound_stream *stream = m_device.machine().sound().first_stream(); stream != NULL; stream = stream->next()) + if (&stream->device() == &m_device) + inputs += stream->input_count(); + return inputs; +} + + +//------------------------------------------------- +// outputs - return the total number of outputs +// for the given device +//------------------------------------------------- + +int device_sound_interface::outputs() const +{ + // scan the list counting streams we own and summing their outputs + int outputs = 0; + for (sound_stream *stream = m_device.machine().sound().first_stream(); stream != NULL; stream = stream->next()) + if (&stream->device() == &m_device) + outputs += stream->output_count(); + return outputs; +} + + +//------------------------------------------------- +// input_to_stream_input - convert a device's +// input index to a stream and the input index +// on that stream +//------------------------------------------------- + +sound_stream *device_sound_interface::input_to_stream_input(int inputnum, int &stream_inputnum) +{ + assert(inputnum >= 0); + + // scan the list looking for streams owned by this device + for (sound_stream *stream = m_device.machine().sound().first_stream(); stream != NULL; stream = stream->next()) + if (&stream->device() == &m_device) + { + if (inputnum < stream->input_count()) + { + stream_inputnum = inputnum; + return stream; + } + inputnum -= stream->input_count(); + } + + // not found + return NULL; +} + + +//------------------------------------------------- +// output_to_stream_output - convert a device's +// output index to a stream and the output index +// on that stream +//------------------------------------------------- + +sound_stream *device_sound_interface::output_to_stream_output(int outputnum, int &stream_outputnum) +{ + assert(outputnum >= 0); + + // scan the list looking for streams owned by this device + for (sound_stream *stream = m_device.machine().sound().first_stream(); stream != NULL; stream = stream->next()) + if (&stream->device() == &device()) + { + if (outputnum < stream->output_count()) + { + stream_outputnum = outputnum; + return stream; + } + outputnum -= stream->output_count(); + } + + // not found + return NULL; +} + + +//------------------------------------------------- +// set_input_gain - set the gain on the given +// input index of the device +//------------------------------------------------- + +void device_sound_interface::set_input_gain(int inputnum, float gain) +{ + int stream_inputnum; + sound_stream *stream = input_to_stream_input(inputnum, stream_inputnum); + if (stream != NULL) + stream->set_input_gain(stream_inputnum, gain); +} + + +//------------------------------------------------- +// set_output_gain - set the gain on the given +// output index of the device +//------------------------------------------------- + +void device_sound_interface::set_output_gain(int outputnum, float gain) +{ + // handle ALL_OUTPUTS as a special case + if (outputnum == ALL_OUTPUTS) + { + for (sound_stream *stream = m_device.machine().sound().first_stream(); stream != NULL; stream = stream->next()) + if (&stream->device() == &device()) + for (int outputnum = 0; outputnum < stream->output_count(); outputnum++) + stream->set_output_gain(outputnum, gain); + } + + // look up the stream and stream output index + else + { + int stream_outputnum; + sound_stream *stream = output_to_stream_output(outputnum, stream_outputnum); + if (stream != NULL) + stream->set_output_gain(stream_outputnum, gain); + } +} + + +//------------------------------------------------- +// inputnum_from_device - return the input number +// that is connected to the given device's output +//------------------------------------------------- + +int device_sound_interface::inputnum_from_device(device_t &source_device, int outputnum) const +{ + int overall = 0; + for (sound_stream *stream = m_device.machine().sound().first_stream(); stream != NULL; stream = stream->next()) + if (&stream->device() == &device()) + for (int inputnum = 0; inputnum < stream->input_count(); inputnum++, overall++) + if (stream->input_source_device(inputnum) == &source_device && stream->input_source_outputnum(inputnum) == outputnum) + return overall; + return -1; +} + + +//------------------------------------------------- +// interface_validity_check - validation for a +// device after the configuration has been +// constructed +//------------------------------------------------- + +void device_sound_interface::interface_validity_check(validity_checker &valid) const +{ + // loop over all the routes + for (const sound_route *route = first_route(); route != NULL; route = route->next()) + { + // find a device with the requested tag + const device_t *target = device().siblingdevice(route->m_target.cstr()); + if (target == NULL) + mame_printf_error("Attempting to route sound to non-existant device '%s'\n", route->m_target.cstr()); + + // if it's not a speaker or a sound device, error + const device_sound_interface *sound; + if (target != NULL && target->type() != SPEAKER && !target->interface(sound)) + mame_printf_error("Attempting to route sound to a non-sound device '%s' (%s)\n", route->m_target.cstr(), target->name()); + } +} + + +//------------------------------------------------- +// interface_pre_start - make sure all our input +// devices are started +//------------------------------------------------- + +void device_sound_interface::interface_pre_start() +{ + // scan all the sound devices + sound_interface_iterator iter(m_device.machine().root_device()); + for (device_sound_interface *sound = iter.first(); sound != NULL; sound = iter.next()) + { + // scan each route on the device + for (const sound_route *route = sound->first_route(); route != NULL; route = route->next()) + { + // see if we are the target of this route; if we are, make sure the source device is started + device_t *target_device = sound->device().siblingdevice(route->m_target); + if (target_device == &m_device && !sound->device().started()) + throw device_missing_dependencies(); + } + } + + // now iterate through devices again and assign any auto-allocated inputs + m_auto_allocated_inputs = 0; + for (device_sound_interface *sound = iter.first(); sound != NULL; sound = iter.next()) + { + // scan each route on the device + for (const sound_route *route = sound->first_route(); route != NULL; route = route->next()) + { + // see if we are the target of this route + device_t *target_device = sound->device().siblingdevice(route->m_target); + if (target_device == &m_device && route->m_input == AUTO_ALLOC_INPUT) + { + const_cast<sound_route *>(route)->m_input = m_auto_allocated_inputs; + m_auto_allocated_inputs += (route->m_output == ALL_OUTPUTS) ? sound->outputs() : 1; + } + } + } +} + + +//------------------------------------------------- +// interface_post_start - verify that state was +// properly set up +//------------------------------------------------- + +void device_sound_interface::interface_post_start() +{ + // iterate over all the sound devices + sound_interface_iterator iter(m_device.machine().root_device()); + for (device_sound_interface *sound = iter.first(); sound != NULL; sound = iter.next()) + { + // scan each route on the device + for (const sound_route *route = sound->first_route(); route != NULL; route = route->next()) + { + // if we are the target of this route, hook it up + device_t *target_device = sound->device().siblingdevice(route->m_target); + if (target_device == &m_device) + { + // iterate over all outputs, matching any that apply + int inputnum = route->m_input; + int numoutputs = sound->outputs(); + for (int outputnum = 0; outputnum < numoutputs; outputnum++) + if (route->m_output == outputnum || route->m_output == ALL_OUTPUTS) + { + // find the output stream to connect from + int streamoutputnum; + sound_stream *outputstream = sound->output_to_stream_output(outputnum, streamoutputnum); + if (outputstream == NULL) + fatalerror("Sound device '%s' specifies route for non-existant output #%d", route->m_target.cstr(), outputnum); + + // find the input stream to connect to + int streaminputnum; + sound_stream *inputstream = input_to_stream_input(inputnum++, streaminputnum); + if (inputstream == NULL) + fatalerror("Sound device '%s' targeted output #%d to non-existant device '%s' input %d", route->m_target.cstr(), outputnum, m_device.tag(), inputnum - 1); + + // set the input + inputstream->set_input(streaminputnum, outputstream, streamoutputnum, route->m_gain); + } + } + } + } +} + + +//------------------------------------------------- +// interface_pre_reset - called prior to +// resetting the device +//------------------------------------------------- + +void device_sound_interface::interface_pre_reset() +{ + // update all streams on this device prior to reset + for (sound_stream *stream = m_device.machine().sound().first_stream(); stream != NULL; stream = stream->next()) + if (&stream->device() == &device()) + stream->update(); +} + + + +//************************************************************************** +// SOUND ROUTE +//************************************************************************** + +//------------------------------------------------- +// sound_route - constructor +//------------------------------------------------- + +device_sound_interface::sound_route::sound_route(int output, int input, float gain, const char *target, UINT32 mixoutput) + : m_next(NULL), + m_output(output), + m_input(input), + m_mixoutput(mixoutput), + m_gain(gain), + m_target(target) +{ +} + + + +//************************************************************************** +// SIMPLE DERIVED MIXER INTERFACE +//************************************************************************** + +//------------------------------------------------- +// device_mixer_interface - constructor +//------------------------------------------------- + +device_mixer_interface::device_mixer_interface(const machine_config &mconfig, device_t &device, int outputs) + : device_sound_interface(mconfig, device), + m_outputs(outputs), + m_mixer_stream(NULL) +{ +} + + +//------------------------------------------------- +// ~device_mixer_interface - destructor +//------------------------------------------------- + +device_mixer_interface::~device_mixer_interface() +{ +} + + +//------------------------------------------------- +// interface_pre_start - perform startup prior +// to the device startup +//------------------------------------------------- + +void device_mixer_interface::interface_pre_start() +{ + // call our parent + device_sound_interface::interface_pre_start(); + + // no inputs? that's weird + if (m_auto_allocated_inputs == 0) + { + logerror("Warning: mixer \"%s\" has no inputs\n", device().tag()); + return; + } + + // generate the output map + m_outputmap.resize(m_auto_allocated_inputs); + + // iterate through all routes that point to us and note their mixer output + sound_interface_iterator iter(m_device.machine().root_device()); + for (device_sound_interface *sound = iter.first(); sound != NULL; sound = iter.next()) + for (const sound_route *route = sound->first_route(); route != NULL; route = route->next()) + { + // see if we are the target of this route + device_t *target_device = sound->device().siblingdevice(route->m_target); + if (target_device == &device() && route->m_input < m_auto_allocated_inputs) + { + int count = (route->m_output == ALL_OUTPUTS) ? sound->outputs() : 1; + for (int output = 0; output < count; output++) + m_outputmap[route->m_input + output] = route->m_mixoutput; + } + } + + // allocate the mixer stream + m_mixer_stream = stream_alloc(m_auto_allocated_inputs, m_outputs, device().machine().sample_rate()); +} + + +//------------------------------------------------- +// interface_post_load - after we load a save +// state be sure to update the mixer stream's +// output sample rate +//------------------------------------------------- + +void device_mixer_interface::interface_post_load() +{ + m_mixer_stream->set_sample_rate(device().machine().sample_rate()); + + // call our parent + device_sound_interface::interface_post_load(); +} + + +//------------------------------------------------- +// mixer_update - mix all inputs to one output +//------------------------------------------------- + +void device_mixer_interface::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + // clear output buffers + for (int output = 0; output < m_outputs; output++) + memset(outputs[output], 0, samples * sizeof(outputs[0][0])); + + // loop over samples + const UINT8 *outmap = &m_outputmap[0]; + for (int pos = 0; pos < samples; pos++) + { + // for each input, add it to the appropriate output + for (int inp = 0; inp < m_auto_allocated_inputs; inp++) + outputs[outmap[inp]][pos] += inputs[inp][pos]; + } +} |