mirror of
https://github.com/ondra-novak/gates_of_skeldal.git
synced 2025-07-05 06:00:33 -04:00
145 lines
3.9 KiB
C++
145 lines
3.9 KiB
C++
#pragma once
|
|
#include "wave_source.h"
|
|
|
|
#include <atomic>
|
|
|
|
|
|
template<typename X>
|
|
class NoCopyObject: public X {
|
|
public:
|
|
using X::X;
|
|
|
|
NoCopyObject(const NoCopyObject &other):X() {}
|
|
NoCopyObject &operator=(const NoCopyObject &other) {
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
template<int channels>
|
|
class WaveMixer {
|
|
public:
|
|
|
|
enum State {
|
|
playing,
|
|
done
|
|
};
|
|
|
|
WaveMixer(std::shared_ptr<WaveSource> source)
|
|
:_src(std::move(source)) {}
|
|
WaveMixer(std::shared_ptr<WaveSource> source, const std::array<float,channels> &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<float, channels> 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<decltype(value)>;
|
|
if constexpr (std::is_same_v<T, float>) {
|
|
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<T, WaveSource::StereoFloat>);
|
|
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[1];
|
|
std::advance(iter, channels-1);
|
|
}
|
|
}
|
|
|
|
}, samples_count, speed);
|
|
_volume_fade = ch;
|
|
return playing;
|
|
}
|
|
|
|
void set_channel_volumes(const std::array<float,channels> &volumes) {
|
|
std::lock_guard _(_mx);
|
|
_volume = volumes;
|
|
}
|
|
|
|
void set_channel_volumes_fade(const std::array<float,channels> &volumes, const std::array<float,channels> &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<WaveSource> get_source() const {return _src;}
|
|
|
|
protected:
|
|
|
|
|
|
|
|
std::shared_ptr<WaveSource> _src;
|
|
mutable NoCopyObject<std::mutex> _mx;
|
|
std::array<float, channels> _volume = {};
|
|
std::array<float, channels> _target_volume = {};
|
|
std::array<float, channels> _volume_change = {};
|
|
bool _volume_fade = false;
|
|
float _speed = 1.0;
|
|
State _state = playing;
|
|
|
|
|
|
|
|
};
|