mame/src/emu/sound.h
couriersud fe1e26a9fb
sound.cpp: Fix missed samples due to state save (#9917) (#9918)
* sound.cpp: Fix missed samples due to state save (#9917)

The PR address #9917. A save state may occur between time slices.
Sound devices through sound.cpp are updated during a timer call every
20ms. When the state is saved, these devices are not updated to the
current machine time. Consequently after a state load the devices have
have a "time lag" since in postload buffer end time is forced to
machine time.
This change will save the last buffer end time so that all outstanding
samples are processed.

This is a core change. I tested it on some drivers. This needs a very
thorough review and I post the PR primarily to document a possible
solution.

* sound.cpp: use "stream.sound_stream" as module name for save_item

* sound.cpp: use presave handler to store end_time()
2022-06-15 12:39:00 +02:00

858 lines
30 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
sound.h
Core sound interface functions and definitions.
****************************************************************************
In MAME, sound is represented as a graph of sound "streams". Each
stream has a fixed number of inputs and outputs, and is responsible
for producing sound on demand.
The graph is driven from the outputs, which are speaker devices.
These devices are updated on a regular basis (~50 times per second),
and when an update occurs, the graph is walked from the speaker
through each input, until all connected streams are up to date.
Individual streams can also be updated manually. This is important
for sound chips and CPU-driven devices, who should force any
affected streams to update prior to making changes.
Sound streams are *not* part of the device execution model. This is
very important to understand. If the process of producing the ouput
stream affects state that might be consumed by an executing device
(e.g., a CPU), then care must be taken to ensure that the stream is
updated frequently enough
The model for timing sound samples is very important and explained
here. Each stream source has a clock (aka sample rate). Each clock
edge represents a sample that is held for the duration of one clock
period. This model has interesting effects:
For example, if you have a 10Hz clock, and call stream.update() at
t=0.91, it will compute 10 samples (for clock edges 0.0, 0.1, 0.2,
..., 0.7, 0.8, and 0.9). And then if you ask the stream what its
current end time is (via stream.sample_time()), it will say t=1.0,
which is in the future, because it knows it will hold that last
sample until 1.0s.
Sound generation callbacks are presented with a std::vector of inputs
and outputs. The vectors contain objects of read_stream_view and
write_stream_view respectively, which wrap access to a circular buffer
of samples. Sound generation callbacks are expected to fill all the
samples described by the outputs' write_stream_view objects. At the
moment, all outputs have the same sample rate, so the number of samples
that need to be generated will be consistent across all outputs.
By default, the inputs will have been resampled to match the output
sample rate, unless otherwise specified.
***************************************************************************/
#pragma once
#ifndef __EMU_H__
#error Dont include this file directly; include emu.h instead.
#endif
#ifndef MAME_EMU_SOUND_H
#define MAME_EMU_SOUND_H
#include "wavwrite.h"
//**************************************************************************
// CONSTANTS
//**************************************************************************
// special sample-rate values
constexpr u32 SAMPLE_RATE_INVALID = 0xffffffff;
constexpr u32 SAMPLE_RATE_INPUT_ADAPTIVE = 0xfffffffe;
constexpr u32 SAMPLE_RATE_OUTPUT_ADAPTIVE = 0xfffffffd;
// anything below this sample rate is effectively treated as "off"
constexpr u32 SAMPLE_RATE_MINIMUM = 50;
//**************************************************************************
// DEBUGGING
//**************************************************************************
// turn this on to enable aggressive assertions and other checks
#ifdef MAME_DEBUG
#define SOUND_DEBUG (1)
#else
#define SOUND_DEBUG (0)
#endif
// if SOUND_DEBUG is on, make assertions fire regardless of MAME_DEBUG
#if (SOUND_DEBUG)
#define sound_assert(x) do { if (!(x)) { osd_printf_error("sound_assert: " #x "\n"); osd_break_into_debugger("sound_assert: " #x "\n"); } } while (0)
#else
#define sound_assert assert
#endif
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> stream_buffer
class stream_buffer
{
// stream_buffer is an internal class, not directly accessed
// outside of the classes below
friend class read_stream_view;
friend class write_stream_view;
friend class sound_stream;
friend class sound_stream_output;
public:
// the one public bit is the sample type
using sample_t = float;
private:
// constructor/destructor
stream_buffer(u32 sample_rate = 48000);
~stream_buffer();
// disable copying of stream_buffers directly
stream_buffer(stream_buffer const &src) = delete;
stream_buffer &operator=(stream_buffer const &rhs) = delete;
// return the current sample rate
u32 sample_rate() const { return m_sample_rate; }
// set a new sample rate
void set_sample_rate(u32 rate, bool resample);
// return the current sample period in attoseconds
attoseconds_t sample_period_attoseconds() const { return m_sample_attos; }
attotime sample_period() const { return attotime(0, m_sample_attos); }
// return the attotime of the current end of buffer
attotime end_time() const { return index_time(m_end_sample); }
// set the ending time (for forced resyncs; generally not used)
void set_end_time(attotime time)
{
m_end_second = time.seconds();
m_end_sample = u32(time.attoseconds() / m_sample_attos);
}
// return the effective buffer size; currently it is a full second of audio
// at the current sample rate, but this maybe change in the future
u32 size() const { return m_sample_rate; }
// read the sample at the given index (clamped); should be valid in all cases
sample_t get(s32 index) const
{
sound_assert(u32(index) < size());
sample_t value = m_buffer[index];
#if (SOUND_DEBUG)
sound_assert(!std::isnan(value));
#endif
return value;
}
// write the sample at the given index (clamped)
void put(s32 index, sample_t data)
{
sound_assert(u32(index) < size());
m_buffer[index] = data;
}
// simple helpers to step indexes
u32 next_index(u32 index) { index++; return (index == size()) ? 0 : index; }
u32 prev_index(u32 index) { return (index == 0) ? (size() - 1) : (index - 1); }
// clamp an index to the size of the buffer; allows for indexing +/- one
// buffers' worth of range
u32 clamp_index(s32 index) const
{
if (index < 0)
index += size();
else if (index >= size())
index -= size();
sound_assert(index >= 0 && index < size());
return index;
}
// fill the buffer with the given value
void fill(sample_t value) { std::fill_n(&m_buffer[0], m_buffer.size(), value); }
// return the attotime of a given index within the buffer
attotime index_time(s32 index) const;
// given an attotime, return the buffer index corresponding to it
u32 time_to_buffer_index(attotime time, bool round_up, bool allow_expansion = false);
// downsample from our buffer into a temporary buffer
void backfill_downsample(sample_t *dest, int samples, attotime newend, attotime newperiod);
// upsample from a temporary buffer into our buffer
void backfill_upsample(sample_t const *src, int samples, attotime prevend, attotime prevperiod);
// internal state
u32 m_end_second; // current full second of the buffer end
u32 m_end_sample; // current sample number within the final second
u32 m_sample_rate; // sample rate of the data in the buffer
attoseconds_t m_sample_attos; // pre-computed attoseconds per sample
std::vector<sample_t> m_buffer; // vector of actual buffer data
#if (SOUND_DEBUG)
public:
// for debugging, provide an interface to write a WAV stream
void open_wav(char const *filename);
void flush_wav();
private:
// internal debugging state
util::wav_file_ptr m_wav_file; // pointer to the current WAV file
u32 m_last_written = 0; // last written sample index
#endif
};
// ======================> read_stream_view
class read_stream_view
{
public:
using sample_t = stream_buffer::sample_t;
protected:
// private constructor used by write_stream_view that allows for expansion
read_stream_view(stream_buffer &buffer, attotime start, attotime end) :
read_stream_view(&buffer, 0, buffer.time_to_buffer_index(end, true, true), 1.0)
{
// start has to be set after end, since end can expand the buffer and
// potentially invalidate start
m_start = buffer.time_to_buffer_index(start, false);
normalize_start_end();
}
public:
// base constructor to simplify some of the code
read_stream_view(stream_buffer *buffer, s32 start, s32 end, sample_t gain) :
m_buffer(buffer),
m_end(end),
m_start(start),
m_gain(gain)
{
normalize_start_end();
}
// empty constructor so we can live in an array or vector
read_stream_view() :
read_stream_view(nullptr, 0, 0, 1.0)
{
}
// constructor that covers the given time period
read_stream_view(stream_buffer &buffer, attotime start, attotime end, sample_t gain) :
read_stream_view(&buffer, buffer.time_to_buffer_index(start, false), buffer.time_to_buffer_index(end, true), gain)
{
}
// copy constructor
read_stream_view(read_stream_view const &src) :
read_stream_view(src.m_buffer, src.m_start, src.m_end, src.m_gain)
{
}
// copy constructor that sets a different start time
read_stream_view(read_stream_view const &src, attotime start) :
read_stream_view(src.m_buffer, src.m_buffer->time_to_buffer_index(start, false), src.m_end, src.m_gain)
{
}
// copy assignment
read_stream_view &operator=(read_stream_view const &rhs)
{
m_buffer = rhs.m_buffer;
m_start = rhs.m_start;
m_end = rhs.m_end;
m_gain = rhs.m_gain;
normalize_start_end();
return *this;
}
// return the local gain
sample_t gain() const { return m_gain; }
// return the sample rate of the data
u32 sample_rate() const { return m_buffer->sample_rate(); }
// return the sample period (in attoseconds) of the data
attoseconds_t sample_period_attoseconds() const { return m_buffer->sample_period_attoseconds(); }
attotime sample_period() const { return m_buffer->sample_period(); }
// return the number of samples represented by the buffer
u32 samples() const { return m_end - m_start; }
// return the starting or ending time of the buffer
attotime start_time() const { return m_buffer->index_time(m_start); }
attotime end_time() const { return m_buffer->index_time(m_end); }
// set the gain
read_stream_view &set_gain(float gain) { m_gain = gain; return *this; }
// apply an additional gain factor
read_stream_view &apply_gain(float gain) { m_gain *= gain; return *this; }
// safely fetch a gain-scaled sample from the buffer
sample_t get(s32 index) const
{
sound_assert(u32(index) < samples());
index += m_start;
if (index >= m_buffer->size())
index -= m_buffer->size();
return m_buffer->get(index) * m_gain;
}
// safely fetch a raw sample from the buffer; if you use this, you need to
// apply the gain yourself for correctness
sample_t getraw(s32 index) const
{
sound_assert(u32(index) < samples());
index += m_start;
if (index >= m_buffer->size())
index -= m_buffer->size();
return m_buffer->get(index);
}
protected:
// normalize start/end
void normalize_start_end()
{
// ensure that end is always greater than start; we'll
// wrap to the buffer length as needed
if (m_end < m_start && m_buffer != nullptr)
m_end += m_buffer->size();
sound_assert(m_end >= m_start);
}
// internal state
stream_buffer *m_buffer; // pointer to the stream buffer we're viewing
s32 m_end; // ending sample index (always >= start)
s32 m_start; // starting sample index
sample_t m_gain; // overall gain factor
};
// ======================> write_stream_view
class write_stream_view : public read_stream_view
{
public:
// empty constructor so we can live in an array or vector
write_stream_view()
{
}
// constructor that covers the given time period
write_stream_view(stream_buffer &buffer, attotime start, attotime end) :
read_stream_view(buffer, start, end)
{
}
// constructor that converts from a read_stream_view
write_stream_view(read_stream_view const &src) :
read_stream_view(src)
{
}
// safely write a sample to the buffer
void put(s32 start, sample_t sample)
{
sound_assert(u32(start) < samples());
m_buffer->put(index_to_buffer_index(start), sample);
}
// write a sample to the buffer, clamping to +/- the clamp value
void put_clamp(s32 index, sample_t sample, sample_t clamp = 1.0)
{
assert(clamp >= sample_t(0));
put(index, std::clamp(sample, -clamp, clamp));
}
// write a sample to the buffer, converting from an integer with the given maximum
void put_int(s32 index, s32 sample, s32 max)
{
put(index, sample_t(sample) * (1.0f / sample_t(max)));
}
// write a sample to the buffer, converting from an integer with the given maximum
void put_int_clamp(s32 index, s32 sample, s32 maxclamp)
{
assert(maxclamp >= 0);
put_int(index, std::clamp(sample, -maxclamp, maxclamp), maxclamp);
}
// safely add a sample to the buffer
void add(s32 start, sample_t sample)
{
sound_assert(u32(start) < samples());
u32 index = index_to_buffer_index(start);
m_buffer->put(index, m_buffer->get(index) + sample);
}
// add a sample to the buffer, converting from an integer with the given maximum
void add_int(s32 index, s32 sample, s32 max)
{
add(index, sample_t(sample) * (1.0f / sample_t(max)));
}
// fill part of the view with the given value
void fill(sample_t value, s32 start, s32 count)
{
if (start + count > samples())
count = samples() - start;
u32 index = index_to_buffer_index(start);
for (s32 sampindex = 0; sampindex < count; sampindex++)
{
m_buffer->put(index, value);
index = m_buffer->next_index(index);
}
}
void fill(sample_t value, s32 start) { fill(value, start, samples() - start); }
void fill(sample_t value) { fill(value, 0, samples()); }
// copy data from another view
void copy(read_stream_view const &src, s32 start, s32 count)
{
if (start + count > samples())
count = samples() - start;
u32 index = index_to_buffer_index(start);
for (s32 sampindex = 0; sampindex < count; sampindex++)
{
m_buffer->put(index, src.get(start + sampindex));
index = m_buffer->next_index(index);
}
}
void copy(read_stream_view const &src, s32 start) { copy(src, start, samples() - start); }
void copy(read_stream_view const &src) { copy(src, 0, samples()); }
// add data from another view to our current values
void add(read_stream_view const &src, s32 start, s32 count)
{
if (start + count > samples())
count = samples() - start;
u32 index = index_to_buffer_index(start);
for (s32 sampindex = 0; sampindex < count; sampindex++)
{
m_buffer->put(index, m_buffer->get(index) + src.get(start + sampindex));
index = m_buffer->next_index(index);
}
}
void add(read_stream_view const &src, s32 start) { add(src, start, samples() - start); }
void add(read_stream_view const &src) { add(src, 0, samples()); }
private:
// given a stream starting offset, return the buffer index
u32 index_to_buffer_index(s32 start) const
{
u32 index = start + m_start;
if (index >= m_buffer->size())
index -= m_buffer->size();
return index;
}
};
// ======================> sound_stream_output
class sound_stream_output
{
#if (SOUND_DEBUG)
friend class sound_stream;
#endif
public:
// construction/destruction
sound_stream_output();
// initialization
void init(sound_stream &stream, u32 index, char const *tag_base);
// no copying allowed
sound_stream_output(sound_stream_output const &src) = delete;
sound_stream_output &operator=(sound_stream_output const &rhs) = delete;
// simple getters
sound_stream &stream() const { sound_assert(m_stream != nullptr); return *m_stream; }
attotime end_time() const { return m_buffer.end_time(); }
u32 index() const { return m_index; }
stream_buffer::sample_t gain() const { return m_gain; }
u32 buffer_sample_rate() const { return m_buffer.sample_rate(); }
// simple setters
void set_gain(float gain) { m_gain = gain; }
// return a friendly name
std::string name() const;
// handle a changing sample rate
void sample_rate_changed(u32 rate) { m_buffer.set_sample_rate(rate, true); }
// return an output view covering a time period
write_stream_view view(attotime start, attotime end) { return write_stream_view(m_buffer, start, end); }
// resync the buffer to the given end time
void set_end_time(attotime end) { m_buffer.set_end_time(end); }
// attempt to optimize resamplers by reusing them where possible
sound_stream_output &optimize_resampler(sound_stream_output *input_resampler);
private:
// internal state
sound_stream *m_stream; // owning stream
stream_buffer m_buffer; // output buffer
u32 m_index; // output index within the stream
stream_buffer::sample_t m_gain; // gain to apply to the output
std::vector<sound_stream_output *> m_resampler_list; // list of resamplers we're connected to
};
// ======================> sound_stream_input
class sound_stream_input
{
#if (SOUND_DEBUG)
friend class sound_stream;
#endif
public:
// construction/destruction
sound_stream_input();
// initialization
void init(sound_stream &stream, u32 index, char const *tag_base, sound_stream_output *resampler);
// no copying allowed
sound_stream_input(sound_stream_input const &src) = delete;
sound_stream_input &operator=(sound_stream_input const &rhs) = delete;
// simple getters
bool valid() const { return (m_native_source != nullptr); }
sound_stream &owner() const { sound_assert(valid()); return *m_owner; }
sound_stream_output &source() const { sound_assert(valid()); return *m_native_source; }
u32 index() const { return m_index; }
stream_buffer::sample_t gain() const { return m_gain; }
stream_buffer::sample_t user_gain() const { return m_user_gain; }
// simple setters
void set_gain(float gain) { m_gain = gain; }
void set_user_gain(float gain) { m_user_gain = gain; }
// return a friendly name
std::string name() const;
// connect the source
void set_source(sound_stream_output *source);
// update and return an reading view
read_stream_view update(attotime start, attotime end);
// tell inputs to apply sample rate changes
void apply_sample_rate_changes(u32 updatenum, u32 downstream_rate);
private:
// internal state
sound_stream *m_owner; // pointer to the owning stream
sound_stream_output *m_native_source; // pointer to the native sound_stream_output
sound_stream_output *m_resampler_source; // pointer to the resampler output
u32 m_index; // input index within the stream
stream_buffer::sample_t m_gain; // gain to apply to this input
stream_buffer::sample_t m_user_gain; // user-controlled gain to apply to this input
};
// ======================> stream_update_delegate
// new-style callback
using stream_update_delegate = delegate<void (sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)>;
// ======================> sound_stream_flags
enum sound_stream_flags : u32
{
// default is no special flags
STREAM_DEFAULT_FLAGS = 0x00,
// specify that updates should be forced to one sample at a time, in real time
// this implicitly creates a timer that runs at the stream's output frequency
// so only use when strictly necessary
STREAM_SYNCHRONOUS = 0x01,
// specify that input streams should not be resampled; stream update handler
// must be able to accommodate multiple strams of differing input rates
STREAM_DISABLE_INPUT_RESAMPLING = 0x02
};
// ======================> sound_stream
class sound_stream
{
friend class sound_manager;
// private common constructopr
sound_stream(device_t &device, u32 inputs, u32 outputs, u32 output_base, u32 sample_rate, sound_stream_flags flags);
public:
// construction/destruction
sound_stream(device_t &device, u32 inputs, u32 outputs, u32 output_base, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags = STREAM_DEFAULT_FLAGS);
virtual ~sound_stream();
// simple getters
sound_stream *next() const { return m_next; }
device_t &device() const { return m_device; }
std::string name() const { return m_name; }
bool input_adaptive() const { return m_input_adaptive || m_synchronous; }
bool output_adaptive() const { return m_output_adaptive; }
bool synchronous() const { return m_synchronous; }
bool resampling_disabled() const { return m_resampling_disabled; }
// input and output getters
u32 input_count() const { return m_input.size(); }
u32 output_count() const { return m_output.size(); }
u32 output_base() const { return m_output_base; }
sound_stream_input &input(int index) { sound_assert(index >= 0 && index < m_input.size()); return m_input[index]; }
sound_stream_output &output(int index) { sound_assert(index >= 0 && index < m_output.size()); return m_output[index]; }
// sample rate and timing getters
u32 sample_rate() const { return (m_pending_sample_rate != SAMPLE_RATE_INVALID) ? m_pending_sample_rate : m_sample_rate; }
attotime sample_time() const { return m_output[0].end_time(); }
attotime sample_period() const { return attotime(0, sample_period_attoseconds()); }
attoseconds_t sample_period_attoseconds() const { return (m_sample_rate != SAMPLE_RATE_INVALID) ? HZ_TO_ATTOSECONDS(m_sample_rate) : ATTOSECONDS_PER_SECOND; }
// set the sample rate of the stream; will kick in at the next global update
void set_sample_rate(u32 sample_rate);
// connect the output 'outputnum' of given input_stream to this stream's input 'inputnum'
void set_input(int inputnum, sound_stream *input_stream, int outputnum = 0, float gain = 1.0f);
// force an update to the current time
void update();
// force an update to the current time, returning a view covering the given time period
read_stream_view update_view(attotime start, attotime end, u32 outputnum = 0);
// apply any pending sample rate changes; should only be called by the sound manager
void apply_sample_rate_changes(u32 updatenum, u32 downstream_rate);
#if (SOUND_DEBUG)
// print one level of the sound graph and recursively tell our inputs to do the same
void print_graph_recursive(int indent, int index);
#endif
protected:
// protected state
std::string m_name; // name of this stream
private:
// perform most of the initialization here
void init_common(u32 inputs, u32 outputs, u32 sample_rate, sound_stream_flags flags);
// if the sample rate has changed, this gets called to update internals
void sample_rate_changed();
// handle updates after a save state load
void postload();
// handle updates before a save state load
void presave();
// re-print the synchronization timer
void reprime_sync_timer();
// timer callback for synchronous streams
void sync_update(s32);
// return a view of 0 data covering the given time period
read_stream_view empty_view(attotime start, attotime end);
// linking information
device_t &m_device; // owning device
sound_stream *m_next; // next stream in the chain
// general information
u32 m_sample_rate; // current live sample rate
u32 m_pending_sample_rate; // pending sample rate for dynamic changes
u32 m_last_sample_rate_update; // update number of last sample rate change
bool m_input_adaptive; // adaptive stream that runs at the sample rate of its input
bool m_output_adaptive; // adaptive stream that runs at the sample rate of its output
bool m_synchronous; // synchronous stream that runs at the rate of its input
bool m_resampling_disabled; // is resampling of input streams disabled?
emu_timer *m_sync_timer; // update timer for synchronous streams
attotime m_last_update_end_time; // last end_time() in update
// input information
std::vector<sound_stream_input> m_input; // list of streams we directly depend upon
std::vector<read_stream_view> m_input_view; // array of output views for passing to the callback
std::vector<std::unique_ptr<sound_stream>> m_resampler_list; // internal list of resamplers
stream_buffer m_empty_buffer; // empty buffer for invalid inputs
// output information
u32 m_output_base; // base index of our outputs, relative to our device
std::vector<sound_stream_output> m_output; // list of streams which directly depend upon us
std::vector<write_stream_view> m_output_view; // array of output views for passing to the callback
// callback information
stream_update_delegate m_callback_ex; // extended callback function
};
// ======================> default_resampler_stream
class default_resampler_stream : public sound_stream
{
public:
// construction/destruction
default_resampler_stream(device_t &device);
// update handler
void resampler_sound_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs);
private:
// internal state
u32 m_max_latency;
};
// ======================> sound_manager
// structure describing an indexed mixer
struct mixer_input
{
device_mixer_interface *mixer; // owning device interface
sound_stream * stream; // stream within the device
int inputnum; // input on the stream
};
class sound_manager
{
friend class sound_stream;
// reasons for muting
static constexpr u8 MUTE_REASON_PAUSE = 0x01;
static constexpr u8 MUTE_REASON_UI = 0x02;
static constexpr u8 MUTE_REASON_DEBUGGER = 0x04;
static constexpr u8 MUTE_REASON_SYSTEM = 0x08;
// stream updates
static const attotime STREAMS_UPDATE_ATTOTIME;
public:
static constexpr int STREAMS_UPDATE_FREQUENCY = 50;
// construction/destruction
sound_manager(running_machine &machine);
~sound_manager();
// getters
running_machine &machine() const { return m_machine; }
int attenuation() const { return m_attenuation; }
const std::vector<std::unique_ptr<sound_stream>> &streams() const { return m_stream_list; }
attotime last_update() const { return m_last_update; }
int sample_count() const { return m_samples_this_update; }
int unique_id() { return m_unique_id++; }
stream_buffer::sample_t compressor_scale() const { return m_compressor_scale; }
// allocate a new stream with a new-style callback
sound_stream *stream_alloc(device_t &device, u32 inputs, u32 outputs, u32 sample_rate, stream_update_delegate callback, sound_stream_flags flags);
// WAV recording
bool is_recording() const { return bool(m_wavfile); }
bool start_recording();
bool start_recording(std::string_view filename);
void stop_recording();
// set the global OSD attenuation level
void set_attenuation(float attenuation);
// mute sound for one of various independent reasons
bool muted() const { return bool(m_muted); }
bool ui_mute() const { return bool(m_muted & MUTE_REASON_UI); }
bool debugger_mute() const { return bool(m_muted & MUTE_REASON_DEBUGGER); }
bool system_mute() const { return bool(m_muted & MUTE_REASON_SYSTEM); }
void ui_mute(bool turn_off) { mute(turn_off, MUTE_REASON_UI); }
void debugger_mute(bool turn_off) { mute(turn_off, MUTE_REASON_DEBUGGER); }
void system_mute(bool turn_off) { mute(turn_off, MUTE_REASON_SYSTEM); }
// return information about the given mixer input, by index
bool indexed_mixer_input(int index, mixer_input &info) const;
// fill the given buffer with 16-bit stereo audio samples
void samples(s16 *buffer);
private:
// set/reset the mute state for the given reason
void mute(bool mute, u8 reason);
// helper to remove items from the orphan list
void recursive_remove_stream_from_orphan_list(sound_stream *stream);
// apply pending sample rate changes
void apply_sample_rate_changes();
// reset all sound chips
void reset();
// pause/resume sound output
void pause();
void resume();
// handle configuration load/save
void config_load(config_type cfg_type, config_level cfg_lvl, util::xml::data_node const *parentnode);
void config_save(config_type cfg_type, util::xml::data_node *parentnode);
// helper to adjust scale factor toward a goal
stream_buffer::sample_t adjust_toward_compressor_scale(stream_buffer::sample_t curscale, stream_buffer::sample_t prevsample, stream_buffer::sample_t rawsample);
// periodic sound update, called STREAMS_UPDATE_FREQUENCY per second
void update(s32 param = 0);
// internal state
running_machine &m_machine; // reference to the running machine
emu_timer *m_update_timer; // timer that runs the update function
std::vector<std::reference_wrapper<speaker_device> > m_speakers;
u32 m_update_number; // current update index; used for sample rate updates
attotime m_last_update; // time of the last update
u32 m_finalmix_leftover; // leftover samples in the final mix
u32 m_samples_this_update; // number of samples this update
std::vector<s16> m_finalmix; // final mix, in 16-bit signed format
std::vector<stream_buffer::sample_t> m_leftmix; // left speaker mix, in native format
std::vector<stream_buffer::sample_t> m_rightmix; // right speaker mix, in native format
stream_buffer::sample_t m_compressor_scale; // current compressor scale factor
int m_compressor_counter; // compressor update counter for backoff
bool m_compressor_enabled; // enable compressor (it will still be calculated for detecting overdrive)
u8 m_muted; // bitmask of muting reasons
bool m_nosound_mode; // true if we're in "nosound" mode
int m_attenuation; // current attentuation level (at the OSD)
int m_unique_id; // unique ID used for stream identification
util::wav_file_ptr m_wavfile; // WAV file for streaming
// streams data
std::vector<std::unique_ptr<sound_stream>> m_stream_list; // list of streams
std::map<sound_stream *, u8> m_orphan_stream_list; // list of orphaned streams
bool m_first_reset; // is this our first reset?
};
#endif // MAME_EMU_SOUND_H