#include "SDTCommonMacros.h"
#include "SDTJSON.h"

/** @file SDTLiquids.h
@defgroup liquids SDTLiquids.h: Liquid sounds
Models and algorithms to simulate sounds generated by liquids: burbling,
splashing, dripping, filling, gushing etc.
@{ */

#ifndef SDT_LIQUIDS_H
#define SDT_LIQUIDS_H

#ifdef __cplusplus
extern "C" {
#endif

/** @defgroup bubbles Bubbles
The main responsible for acoustic emission in water and other liquids,
rather than the liquid mass on its own, is the gas trapped inside emerging
as a population of bubbles. From a physical point of view, a spherical bubble
acts as an exponentially decaying sinusoidal oscillator. Frequency, decay time
and relative amplitude of each bubble can be derived from its radius and depth.

When the bubble is formed close to the surface and therefore the effective mass
around the liquid is reduced, the oscillating frequency rises
and a characteristic ''blooping'' sound is generated. The amount of blooping can
be set as an independent parameter in the model.
@{ */

/** @brief Opaque data structure representing a bubble object */
typedef struct SDTBubble SDTBubble;

/** @brief Object constructor.
@return Pointer to the new instance */
extern SDTBubble *SDTBubble_new();

/** @brief Object destructor.
@param[in] x Pointer to the instance to destroy */
extern void SDTBubble_free(SDTBubble *x);

/** @brief Deep-copies a bouncing process.
@param[in] dest Pointer to the instance to modify
@param[in] src Pointer to the instance to copy
@param[in] unsafe If false, do not perform any memory-related changes
@return Pointer to destination instance */
extern SDTBubble *SDTBubble_copy(SDTBubble *dest, const SDTBubble *src,
                                 unsigned char unsafe);

/** @brief Registers a bouncing process into the bouncing processes list with a
unique ID.
@param[in] x Bubble instance to register
@param[in] key Unique ID assigned to the bouncing process instance
@return Zero on success, otherwise one */
extern int SDT_registerBubble(SDTBubble *x, const char *key);

/** @brief Queries the bouncing processes list by its unique ID.
If a bouncing process with the ID is present, a pointer to the bouncing process
is returned. Otherwise, a NULL pointer is returned.
@param[in] key Unique ID assigned to the bouncing process instance
@return Bubble instance pointer */
extern SDTBubble *SDT_getBubble(const char *key);

/** @brief Unregisters a bouncing process from the bouncing processes list. If a
bouncing process with the given ID is present, it is unregistered from the list.
@param[in] key Unique ID of the bouncing process instance to unregister
@return Zero on success, otherwise one */
extern int SDT_unregisterBubble(const char *key);

/** @brief Gets the bubble radius.
@return Radius of the bubble */
extern double SDTBubble_getRadius(const SDTBubble *x);

/** @brief Gets the amount of blooping.
@return Amount of blooping of the bubble */
extern double SDTBubble_getRiseFactor(const SDTBubble *x);

/** @brief Gets the bubble depth.
@return Depth of the bubble */
extern double SDTBubble_getDepth(const SDTBubble *x);

/** @brief Represent a bouncing process as a JSON object.
@param[in] x Pointer to the instance
@return JSON object */
extern json_value *SDTBubble_toJSON(const SDTBubble *x);

/** @brief Initialize a bouncing process from a JSON object.
@param[in] x Pointer to the instance
@return JSON object */
extern SDTBubble *SDTBubble_fromJSON(const json_value *x);

/** @brief Set parameters of a bouncing process from a JSON object.
@param[in] x Pointer to the instance
@param[in] j JSON object
@param[in] unsafe If false, do not perform any memory-related changes
@return Pointer to destination instance */
extern SDTBubble *SDTBubble_setParams(SDTBubble *x, const json_value *j,
                                      unsigned char unsafe);

/** @brief Sets the bubble radius.
@param[in] f Bubble radius, in m [0.00015, 0.150] */
extern void SDTBubble_setRadius(SDTBubble *x, double f);

/** @brief Sets the bubble depth.
@param[in] f Bubble depth [0, 1]. 0 means very deep, 1 means touching the
surface. */
extern void SDTBubble_setDepth(SDTBubble *x, double f);

/** @brief Sets the amount of blooping.
@param[in] f Rise factor, positive scalar. Typical value for bubbles in water =
0.1 */
extern void SDTBubble_setRiseFactor(SDTBubble *x, double f);

/** @brief Triggers a new bubble */
extern void SDTBubble_trigger(SDTBubble *x);

/** @brief Sets bubble amplitude to the maximum instead of computing it from
 * radius and depth. */
extern void SDTBubble_normAmp(SDTBubble *x);

/** @brief Signal processing routine.
Call this function at sample rate to obtain a bubble sound.
@return Output sample */
extern double SDTBubble_dsp(SDTBubble *x);

/** @} */

/** @defgroup fluidflow Fluid flow
Rich and complex liquid sound simulations can be generated
through a stochastic population of bubbles, modeled by a sinusoidal oscillator
bank with each voice modulated in amplitude and frequency according to desired
probability distributions. A simple stochastic algorithm controls the behavior
of the bubble population: Bubble generation rate follows a Bernoulli process,
while radius and depth for each new bubble are chosen at random.
To limit the presence of sudden peaks and glitches, voices are updated based on
their age: The bubble with the lowest amplitude gets "killed" in favor of the
new one.
@{ */

/** @brief Opaque data structure representing a fluid flow object */
typedef struct SDTFluidFlow SDTFluidFlow;

#define SDT_FLUIDFLOW_NBUBBLES_DEFAULT 64

/** @brief Object constructor.
@param[in] Number of voices in the oscillator bank
@return Pointer to the new instance */
extern SDTFluidFlow *SDTFluidFlow_new(int nBubbles);

/** @brief Object destructor.
@param[in] x Poiter to the instance to destroy */
extern void SDTFluidFlow_free(SDTFluidFlow *x);

/** @brief Deep-copies an fluid flow.
@param[in] dest Pointer to the instance to modify
@param[in] src Pointer to the instance to copy
@param[in] unsafe If false, do not perform any memory-related changes
@return Pointer to destination instance */
extern SDTFluidFlow *SDTFluidFlow_copy(SDTFluidFlow *dest,
                                       const SDTFluidFlow *src,
                                       unsigned char unsafe);

/** @brief Registers a fluid flow into the fluid flows list with a unique ID.
@param[in] x FluidFlow instance to register
@param[in] key Unique ID assigned to the fluid flow instance
@return Zero on success, otherwise one */
extern int SDT_registerFluidFlow(SDTFluidFlow *x, const char *key);

/** @brief Queries the fluid flows list by its unique ID.
If an fluid flow with the ID is present, a pointer to the fluid flow is
returned. Otherwise, a NULL pointer is returned.
@param[in] key Unique ID assigned to the fluid flow instance
@return FluidFlow instance pointer */
extern SDTFluidFlow *SDT_getFluidFlow(const char *key);

/** @brief Unregisters an fluid flow from the fluid flows list.
If an fluid flow with the given ID is present, it is unregistered from the list.
@param[in] key Unique ID of the fluid flow instance to
unregister
@return Zero on success, otherwise one */
extern int SDT_unregisterFluidFlow(const char *key);

/** @brief Gets the number of bubbles.
@return Number of voices in the oscillator bank */
extern int SDTFluidFlow_getNBubbles(const SDTFluidFlow *x);

/** @brief Gets the minimum radius for the bubble population.
@return Minimum radius of the generated bubbles, in m [0.00015, 0.150] */
extern double SDTFluidFlow_getMinRadius(const SDTFluidFlow *x);

/** @brief Gets the maximum radius for the bubble population.
@return Maximum radius of the generated bubbles, in m [0.00015, 0.150] */
extern double SDTFluidFlow_getMaxRadius(const SDTFluidFlow *x);

/** @brief Gets the gamma factor for the radius assignment
@return Radius gamma factor. O to 1 = bigger bubbles, > 1 = smaller bubbles */
extern double SDTFluidFlow_getExpRadius(const SDTFluidFlow *x);

/** @brief Gets the minimum depth value for the bubble population.
@return Minimum depth value of the generated bubbles, [0, 1] */
extern double SDTFluidFlow_getMinDepth(const SDTFluidFlow *x);

/** @brief Gets the maximum depth value for the bubble population.
@return Maximum depth value of the generated bubbles, [0, 1] */
extern double SDTFluidFlow_getMaxDepth(const SDTFluidFlow *x);

/** @brief Gets the gamma factor for the depth assignment
@return Depth gamma factor. O to 1 = shallower bubbles, > 1 = deeper
bubbles */
extern double SDTFluidFlow_getExpDepth(const SDTFluidFlow *x);

/** @brief Gets the amount of blooping for the bubble population
@return Rise factor. Typical value for water = 0.1 */
extern double SDTFluidFlow_getRiseFactor(const SDTFluidFlow *x);

/** @brief Bubbles deeper than this threshold do not rise in frequency
@return Rise cutoff, [0, 1] */
extern double SDTFluidFlow_getRiseCutoff(const SDTFluidFlow *x);

/** @brief Gets the amount of generated bubbles per second.
@return Average number of bubbles per second */
extern double SDTFluidFlow_getAvgRate(const SDTFluidFlow *x);

/** @brief Represent a fluid flow as a JSON object.
@param[in] x Pointer to the instance
@return JSON object */
extern json_value *SDTFluidFlow_toJSON(const SDTFluidFlow *x);

/** @brief Initialize a fluid flow from a JSON object.
@param[in] x Pointer to the instance
@return JSON object */
extern SDTFluidFlow *SDTFluidFlow_fromJSON(const json_value *x);

/** @brief Set parameters of a fluid flow from a JSON object.
@param[in] x Pointer to the instance
@param[in] j JSON object
@param[in] unsafe If false, do not perform any memory-related changes
@return Pointer to destination instance */
extern SDTFluidFlow *SDTFluidFlow_setParams(SDTFluidFlow *x,
                                            const json_value *j,
                                            unsigned char unsafe);

/** @brief Sets the number of bubbles.
@param[in] f Number of voices in the oscillator bank */
extern void SDTFluidFlow_setNBubbles(SDTFluidFlow *x, int f);

/** @brief Sets the minimum radius for the bubble population.
@param[in] f Minimum radius of the generated bubbles, in m [0.00015, 0.150] */
extern void SDTFluidFlow_setMinRadius(SDTFluidFlow *x, double f);

/** @brief Sets the maximum radius for the bubble population.
@param[in] f Maximum radius of the generated bubbles, in m [0.00015, 0.150] */
extern void SDTFluidFlow_setMaxRadius(SDTFluidFlow *x, double f);

/** @brief Sets the gamma factor for the radius assignment
@param[in] f Radius gamma factor. O to 1 = bigger bubbles, > 1 = smaller bubbles
*/
extern void SDTFluidFlow_setExpRadius(SDTFluidFlow *x, double f);

/** @brief Sets the minimum depth value for the bubble population.
@param[in] f Minimum depth value of the generated bubbles, [0, 1] */
extern void SDTFluidFlow_setMinDepth(SDTFluidFlow *x, double f);

/** @brief Sets the maximum depth value for the bubble population.
@param[in] f Maximum depth value of the generated bubbles, [0, 1] */
extern void SDTFluidFlow_setMaxDepth(SDTFluidFlow *x, double f);

/** @brief Sets the gamma factor for the depth assignment
@param[in] f Depth gamma factor. O to 1 = shallower bubbles, > 1 = deeper
bubbles */
extern void SDTFluidFlow_setExpDepth(SDTFluidFlow *x, double f);

/** @brief Sets the amount of blooping for the bubble population
@param[in] f Rise factor. Typical value for water = 0.1 */
extern void SDTFluidFlow_setRiseFactor(SDTFluidFlow *x, double f);

/** @brief Bubbles deeper than this threshold do not rise in frequency
@param[in] f Rise cutoff, [0, 1] */
extern void SDTFluidFlow_setRiseCutoff(SDTFluidFlow *x, double f);

/** @brief Sets the amount of generated bubbles per second.
@param[in] f Average number of bubbles per second */
extern void SDTFluidFlow_setAvgRate(SDTFluidFlow *x, double f);

/** @brief Signal processing routine.
Call this function at sample rate to obtain a liquid sound.
@return Output sample */
extern double SDTFluidFlow_dsp(SDTFluidFlow *x);

/** @} */

#ifdef __cplusplus
};
#endif

#endif

/** @} */