#ifndef STAR_MIXER_HPP #define STAR_MIXER_HPP #include "StarAudio.hpp" #include "StarThread.hpp" #include "StarList.hpp" #include "StarMap.hpp" #include "StarSet.hpp" #include "StarVector.hpp" #include "StarMaybe.hpp" namespace Star { STAR_CLASS(AudioInstance); STAR_CLASS(Mixer); struct RampedValue { float value; float target; float velocity; }; enum class MixerGroup : uint8_t { Effects, Music, Cinematic, Instruments }; class AudioInstance { public: AudioInstance(Audio const& audio); Maybe position() const; void setPosition(Maybe position); // If the audio has no position, sets the position to zero before translating void translate(Vec2F const& distance); float rangeMultiplier() const; void setRangeMultiplier(float rangeMultiplier); void setVolume(float targetValue, float rampTime = 0.0f); void setPitchMultiplier(float targetValue, float rampTime = 0.0f); // Returns the currently remaining loops int loops() const; // Sets the remaining loops, set to 0 to stop looping void setLoops(int loops); // Returns the current audio playing time position double currentTime() const; // Total length of time of the audio in seconds double totalTime() const; // Seeks the audio to the current time in seconds void seekTime(double time); // The MixerGroup defaults to Effects MixerGroup mixerGroup() const; void setMixerGroup(MixerGroup mixerGroup); // If set, uses wall clock time in milliseconds to set precise start and stop // times for the AudioInstance void setClockStart(Maybe clockStartTime); void setClockStop(Maybe clockStopTime, int64_t fadeOutTime = 0); void stop(float rampTime = 0.0f); bool finished() const; private: friend class Mixer; mutable Mutex m_mutex; Audio m_audio; MixerGroup m_mixerGroup; RampedValue m_volume; float m_pitchMultiplier; float m_pitchMultiplierTarget; float m_pitchMultiplierVelocity; int m_loops; bool m_stopping; bool m_finished; Maybe m_position; float m_rangeMultiplier; Maybe m_clockStart; Maybe m_clockStop; int64_t m_clockStopFadeOut; }; // Thread safe mixer class with basic effects support. class Mixer { public: typedef function ExtraMixFunction; typedef function EffectFunction; typedef function PositionalAttenuationFunction; Mixer(unsigned sampleRate, unsigned channels); unsigned sampleRate() const; unsigned channels() const; // Construct a really crappy low-pass filter based on averaging EffectFunction lowpass(size_t avgSize) const; // Construct a very simple echo filter. EffectFunction echo(float time, float dry, float wet) const; // Adds / removes effects that affect all playback. void addEffect(String const& effectName, EffectFunction effectFunction, float rampTime); void removeEffect(String const& effectName, float rampTime); StringList currentEffects(); bool hasEffect(String const& effectName); // Global speed void setSpeed(float speed); // Global volume void setVolume(float volume, float rampTime); // per mixer group volume void setGroupVolume(MixerGroup group, float targetValue, float rampTime = 0.0f); void play(AudioInstancePtr sample); void stopAll(float rampTime); // Reads pending audio data. This is thread safe with the other Mixer // methods, but only one call to read may be active at a time. void read(int16_t* samples, size_t frameCount, ExtraMixFunction extraMixFunction = {}); // Call within the main loop of the program using Mixer, calculates // positional attenuation of audio and does cleanup. void update(float dt, PositionalAttenuationFunction positionalAttenuationFunction = {}); private: struct EffectInfo { EffectFunction effectFunction; float amount; float velocity; bool finished; }; struct AudioState { List positionalChannelVolumes; }; Mutex m_mutex; unsigned m_sampleRate; unsigned m_channels; RampedValue m_volume; Mutex m_queueMutex; HashMap m_audios; Mutex m_effectsMutex; StringMap> m_effects; List m_mixBuffer; Map m_groupVolumes; atomic m_speed; }; } #endif