mirror of
https://github.com/holub/mame
synced 2025-06-15 08:57:05 +03:00
148 lines
5.5 KiB
ReStructuredText
148 lines
5.5 KiB
ReStructuredText
Audio effects
|
|
=============
|
|
|
|
.. contents:: :local:
|
|
|
|
|
|
1. Generalities
|
|
---------------
|
|
|
|
The audio effects are effects that are applied to the sound between
|
|
the speaker devices and the actual sound output. In the current
|
|
implementation the effect chain is fixed (but not the effect
|
|
parameters), and the parameters are stored in the cfg files. They are
|
|
honestly not really designed for extensibility yet, if ever.
|
|
|
|
Adding an effect requires working on four parts:
|
|
|
|
* audio_effects/aeffects.* for effect object creation and "publishing"
|
|
* audio_effects/youreffect.* for the effect implementation
|
|
* frontend/mame/ui/audioeffects.cpp to be able to instantiate the effect configuration menu
|
|
* frontend/mame/ui/audioyoureffect.* to implement the effect configuration menu
|
|
|
|
2. audio_effects/aeffects.*
|
|
---------------------------
|
|
|
|
The audio_effect class in the aeffect sources provides three things:
|
|
|
|
* an enum value to designate the effect type and which must match its
|
|
position in the chain (iow, the effect chain follows the enum order),
|
|
in the .h
|
|
* the effect name in the audio_effect::effect_names array in the .cpp
|
|
* the creation of a correct effect object in audio_effect::create in the .cpp
|
|
|
|
|
|
|
|
3. audio_effects/youreffect.*
|
|
-----------------------------
|
|
|
|
This is where you implement the effect. It takes the shape of an
|
|
audio_effect_youreffect class which derives from audio_effect.
|
|
|
|
The methods to implement are:
|
|
|
|
.. code-block:: C++
|
|
|
|
audio_effect_youreffect(u32 sample_rate, audio_effect *def);
|
|
|
|
virtual int type() const override;
|
|
virtual void config_load(util::xml::data_node const *ef_node) override;
|
|
virtual void config_save(util::xml::data_node *ef_node) const override;
|
|
virtual void default_changed() override;
|
|
virtual u32 history_size() const; // optional
|
|
|
|
The constructor must pass the parameters to ``audio_effect`` and
|
|
initialize the effect parameters. ``type`` must return the enum value
|
|
for the effect. ``config_load`` and ``config_save`` should load or
|
|
save the effect parameters from/to the cfg file xml tree.
|
|
``default_changed`` is called when the parameters in ``m_default`` are
|
|
changed and the parameters may need to be updated. ``history_size``
|
|
allows to tell how many samples should still be available of the
|
|
previous input frame. Note that this number must not depend on the
|
|
parameters and only on the sample rate.
|
|
|
|
An effect has a number of parameters that can come from three sources:
|
|
|
|
* fixed default value
|
|
* equivalent effect object from the default effect chain
|
|
* user setting through the UI
|
|
|
|
The first two are recognized through the value of ``m_default`` which
|
|
gets the value of ``def`` in the constructor. When it's nullptr, the
|
|
value to use when not set by the user is the fixed one, otherwise it's
|
|
the one in ``m_default``.
|
|
|
|
At minimum an effect should have a parameter allowing to bypass it.
|
|
|
|
Managing a parameter uses four methods:
|
|
|
|
* ``type param() const;`` returns the current parameter value
|
|
* ``void set_param(type value);`` sets the current parameter value and marks it as set by the user
|
|
* ``bool isset_param() const;`` returns true is the parameter was set by the user
|
|
* ``void reset_param();`` resets the parameter to the default value (from m_default or fixed) and marks it as not set by the user
|
|
|
|
``config_save`` must only save the user-set parameters.
|
|
``config_load`` must retrieve the parameters that are present and mark
|
|
them as set by the user and reset all the others.
|
|
|
|
Finally the actual implementation goes into the ``apply`` method:
|
|
|
|
.. code-block:: C++
|
|
|
|
virtual void apply(const emu::detail::output_buffer_flat<sample_t> &src, emu::detail::output_buffer_flat<sample_t> &dest) override;
|
|
|
|
That method takes two buffers with the same number of channels and has
|
|
to apply the effect to ``src`` to produce ``dest``. The
|
|
``output_buffer_flat`` is non-interleaved with independant per-channel
|
|
buffers.
|
|
|
|
To make bypassing easier, the ``copy(src, dest)`` method of
|
|
audio_effect allows to copy the samples from ``src`` to ``dest``
|
|
without changing them.
|
|
|
|
The effect application part should looks like:
|
|
|
|
.. code-block:: C++
|
|
|
|
u32 samples = src.available_samples();
|
|
dest.prepare_space(samples);
|
|
u32 channels = src.channels();
|
|
|
|
// generate channels * samples results and put them in dest
|
|
|
|
dest.commit(samples);
|
|
|
|
To get pointers to the buffers, one uses:
|
|
|
|
.. code-block:: C++
|
|
|
|
const sample_t *source = src.ptrs(channel, source_index); // source_index must be in [-history_size()..samples-1]
|
|
sample_t *destination = dest.ptrw(channel, destination_index); // destination_index must be in [0..samples-1]
|
|
|
|
The samples pointed by source and destination are contiguous. The
|
|
number of channels will not change from one apply call to another, the
|
|
number of samples will vary though. Also the call happens in a
|
|
different thread than the main thread and also in a different thread
|
|
than the parameter setting calls are made from.
|
|
|
|
|
|
|
|
|
|
4. frontend/mame/ui/audioeffects.cpp
|
|
------------------------------------
|
|
|
|
Here it suffices to add a creation of the menu
|
|
menu_audio_effect_youreffect in menu_audio_effects::handle. The menu
|
|
effect will pick the effect names from audio_effect (in aeffect.*).
|
|
|
|
|
|
5. frontend/mame/ui/audioyoureffect.*
|
|
-------------------------------------
|
|
|
|
This is used to implement the configuration menu for the effect. It's
|
|
a little complicated because it needs to generate the list of
|
|
parameters and their values, set left or right arrow flags depending
|
|
on the possible modifications, dim them (FLAG_INVERT) when not set by
|
|
the user, and manage the arrows and clear keys to change them. Just
|
|
copy an existing one and change it as needed.
|