#pragma once #include "wave_source.h" #include template class NoCopyObject: public X { public: using X::X; NoCopyObject(const NoCopyObject &other):X() {} NoCopyObject &operator=(const NoCopyObject &other) { return *this; } }; template class WaveMixer { public: enum State { playing, done }; WaveMixer(std::shared_ptr source) :_src(std::move(source)) {} WaveMixer(std::shared_ptr source, const std::array &volumes, float speed = 1.0) :_src(std::move(source)) ,_volume{volumes} ,_speed(speed) {} static_assert(channels > 0); ///output to buffer by adding values (mixing) /** * @param buffer sound buffer - add values here * @param samples_count count of samples in buffer (so samples * channels for count of floats) * @retval true still playing * @retval false done playing */ bool output_mix(float *buffer, std::size_t samples_count) { std::array vol; float speed; { std::lock_guard _(_mx); if (_state == done) { return 0; } vol = _volume; speed = _speed; } bool ch = false; auto do_volume_fade = [this,&ch](int c){ float d = _target_volume[c] - _volume[c]; if (d > 0) { d = std::min(d, _volume_change[c]); ch = true; } else if (d < 0) { d = std::max(d, -_volume_change[c]); ch = true; } _volume[c] += d; }; auto iter = buffer; bool playing = _src->output([&](const auto &value){ using T = std::decay_t; if constexpr (std::is_same_v) { for (int c = 0; c < channels; ++c) { if (_volume_fade) do_volume_fade(c); float v = value * vol[c]; v = v + (*iter); (*iter) = v; ++iter; } } else { //TODO: handle 1 channel audio static_assert(std::is_same_v); if (_volume_fade) for (int c =0; c< channels; ++c) do_volume_fade(c); if constexpr(channels == 1) { float v = (value.left + value.right) * 0.5; (*iter) += v * vol[0]; } else { (*iter) += value.left * vol[0]; ++iter; (*iter) += value.right * vol[0]; ++iter; } } }, samples_count, speed); _volume_fade = ch; return playing; } void set_channel_volumes(const std::array &volumes) { std::lock_guard _(_mx); _volume = volumes; } void set_channel_volumes_fade(const std::array &volumes, const std::array &change_speed) { std::lock_guard _(_mx); _volume_fade = true; _target_volume = volumes; _volume_change = change_speed; } void set_channel_volume(int channel, float volume) { std::lock_guard _(_mx); if (channel >= 0 && channel < channels) { _volume[channel] = volume; } } void set_speed(float speed) { std::lock_guard _(_mx); _speed = speed; } std::shared_ptr get_source() const {return _src;} protected: std::shared_ptr _src; mutable NoCopyObject _mx; std::array _volume = {}; std::array _target_volume = {}; std::array _volume_change = {}; bool _volume_fade = false; float _speed = 1.0; State _state = playing; };