#pragma once #include "wave_mixer.h" #include "sound_filter.h" #include #include #include template class SoundMixer { public: void mix_to_buffer(float *buffer, std::size_t samples_len) { std::lock_guard _(_mx); for (std::size_t i = 0; i < _tracks.size(); ++i) { Track &t = _tracks[i]; bool playing = t.wave.output_mix(buffer, samples_len); if (!playing) { Track &b = _tracks.back(); if (&b != &t) std::swap(b,t); _tracks.pop_back(); --i; } } visit_all_filters([&](BassTrebleFilter &flt, auto idx){ flt.processBuffer(buffer, samples_len); }); for (std::size_t i = 0; i < samples_len; ++i) { for (int j = 0; j < channels; ++j) { float f = buffer[i*channels+j] * clip_factor; if (f > 1.0) { clip_factor *= clip_factor / f; f = 1.0; } else if (f < -1.0) { clip_factor *= -clip_factor / f; f = -1.0; } buffer[i*channels+j] = f; } } ++_calls; _calls.notify_all(); if (clip_factor<1) clip_factor*=1.001f; } void set_mix_freq(int freq) { visit_all_filters([&](BassTrebleFilter &flt, auto){ flt.setSampleFreq(static_cast(freq)); }); } void set_bass(float val) { visit_all_filters([&](BassTrebleFilter &flt, auto){ flt.setBass(val); }); } void set_treble(float val) { visit_all_filters([&](BassTrebleFilter &flt, auto){ flt.setTreble(val); }); } void wait_for_advance_write_pos() { auto v = _calls.load(); _calls.wait(v); } template &> Fn> bool visit_track(int trackID, Fn &&fn) { std::lock_guard _(_mx); Track *t = find_track_by_id(trackID); if (t) { fn(t->wave); return true; } else { return false; } } template &> Fn> bool visit_track(int trackID, Fn &&fn) const { std::lock_guard _(_mx); const Track *t = find_track_by_id(trackID); if (t) { fn(t->wave); return true; } else { return false; } } void set_track(int trackID, WaveMixer wave) { std::lock_guard _(_mx); Track *t = find_track_by_id(trackID); if (t) t->wave = std::move(wave); else _tracks.push_back({trackID, std::move(wave)}); } struct Track { int trackID; WaveMixer wave; }; void set_tracks(Track *tracks, int count) { std::lock_guard _(_mx); for (int i = 0; i < count; ++i) { Track *t = find_track_by_id(tracks[i].trackID); if (t) t->wave = std::move(tracks[i].wave); else _tracks.push_back(std::move(tracks[i])); } } protected: Track *find_track_by_id(int track) { auto iter = std::find_if(_tracks.begin(), _tracks.end(), [&](const Track &t){ return t.trackID == track; }); if (iter == _tracks.end()) { return nullptr; } else { return &(*iter); } } const Track *find_track_by_id(int track) const { auto iter = std::find_if(_tracks.begin(), _tracks.end(), [&](const Track &t){ return t.trackID == track; }); if (iter == _tracks.end()) { return nullptr; } else { return &(*iter); } } template void visit_all_filters_idx(Fn &&fn, std::integer_sequence) { (fn(_filters[idx], std::integral_constant{}),...); } template void visit_all_filters(Fn &&fn) { visit_all_filters_idx(std::forward(fn), std::make_integer_sequence{}); } std::mutex _mx; std::vector _tracks; std::atomic _calls = {0}; std::array _filters; float clip_factor = 1.0; };