summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound.h')
-rw-r--r--src/emu/sound.h140
1 files changed, 82 insertions, 58 deletions
diff --git a/src/emu/sound.h b/src/emu/sound.h
index c3027b28ba6..25944847565 100644
--- a/src/emu/sound.h
+++ b/src/emu/sound.h
@@ -8,47 +8,47 @@
****************************************************************************
- 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.
+ 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.
***************************************************************************/
@@ -81,7 +81,11 @@ constexpr u32 SAMPLE_RATE_MINIMUM = 50;
//**************************************************************************
// 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)
@@ -346,6 +350,7 @@ protected:
class write_stream_view : public read_stream_view
{
+
public:
// empty constructor so we can live in an array or vector
write_stream_view()
@@ -364,7 +369,7 @@ public:
{
}
- // safely write a gain-applied sample to the buffer
+ // safely write a sample to the buffer
void put(s32 index, sample_t sample)
{
sound_assert(u32(index) < samples());
@@ -374,7 +379,33 @@ public:
m_buffer->put(index, sample);
}
- // safely add a gain-applied sample to the buffer
+ // write a sample to the buffer, clamping to +/- the clamp value
+ void put_clamp(s32 index, sample_t sample, sample_t clamp = 1.0)
+ {
+ if (sample > clamp)
+ sample = clamp;
+ if (sample < -clamp)
+ sample = -clamp;
+ put(index, sample);
+ }
+
+ // 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)
+ {
+ if (sample > maxclamp)
+ sample = maxclamp;
+ else if (sample < -maxclamp)
+ sample = -maxclamp;
+ put_int(index, sample, maxclamp);
+ }
+
+ // safely add a sample to the buffer
void add(s32 index, sample_t sample)
{
sound_assert(u32(index) < samples());
@@ -384,6 +415,12 @@ public:
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)
{
@@ -539,10 +576,7 @@ private:
};
-// ======================> stream_update_legacy_delegate/stream_update_delegate
-
-// old-style callback; eventually should be deprecated
-using stream_update_legacy_delegate = delegate<void (sound_stream &stream, stream_sample_t const * const *inputs, stream_sample_t * const *outputs, int samples)>;
+// ======================> 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)>;
@@ -577,7 +611,6 @@ class sound_stream
public:
// construction/destruction
- sound_stream(device_t &device, u32 inputs, u32 outputs, u32 output_base, u32 sample_rate, stream_update_legacy_delegate callback, sound_stream_flags flags = STREAM_DEFAULT_FLAGS);
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();
@@ -643,9 +676,6 @@ private:
// timer callback for synchronous streams
void sync_update(void *, s32);
- // new callback which wrapps calls through to the old-style callbacks
- void stream_update_legacy(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs);
-
// return a view of 0 data covering the given time period
read_stream_view empty_view(attotime start, attotime end);
@@ -665,7 +695,6 @@ private:
// input information
std::vector<sound_stream_input> m_input; // list of streams we directly depend upon
- std::vector<stream_sample_t *> m_input_array; // array of inputs for passing to the callback
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
@@ -673,12 +702,10 @@ private:
// 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<stream_sample_t *> m_output_array; // array of outputs for passing to the callback
std::vector<write_stream_view> m_output_view; // array of output views for passing to the callback
// callback information
- stream_update_legacy_delegate m_callback; // callback function
- stream_update_delegate m_callback_ex; // extended callback function
+ stream_update_delegate m_callback_ex; // extended callback function
};
@@ -737,9 +764,6 @@ public:
int sample_count() const { return m_samples_this_update; }
int unique_id() { return m_unique_id++; }
- // allocate a new stream with the old-style callback
- sound_stream *stream_alloc_legacy(device_t &device, u32 inputs, u32 outputs, u32 sample_rate, stream_update_legacy_delegate callback);
-
// 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);