// // CompoundSource.hpp // Clock Signal // // Created by Thomas Harte on 19/12/2017. // Copyright 2017 Thomas Harte. All rights reserved. // #ifndef CompoundSource_h #define CompoundSource_h #include "SampleSource.hpp" #include #include namespace Outputs { namespace Speaker { /*! A CompoundSource adds together the sound generated by multiple individual SampleSources. An owner may optionally assign relative volumes. */ template class CompoundSource: public Outputs::Speaker::SampleSource { public: CompoundSource(T &... sources) : source_holder_(sources...) { // Default: give all sources equal volume. const float volume = 1.0f / static_cast(source_holder_.size()); for(std::size_t c = 0; c < source_holder_.size(); ++c) { volumes_.push_back(volume); } } void get_samples(std::size_t number_of_samples, std::int16_t *target) { source_holder_.get_samples(number_of_samples, target); } void skip_samples(const std::size_t number_of_samples) { source_holder_.skip_samples(number_of_samples); } void set_sample_volume_range(int16_t range) { volume_range_ = range; push_volumes(); source_holder_.set_scaled_volume_range(range, volumes_.data()); } /*! Sets the relative volumes of the various sources underlying this compound. The caller should ensure that the number of items supplied matches the number of sources and that the values in it sum to 1.0. */ void set_relative_volumes(const std::vector &volumes) { assert(volumes.size() == source_holder_.size()); volumes_ = volumes; push_volumes(); } private: void push_volumes() { source_holder_.set_scaled_volume_range(volume_range_, volumes_.data()); } template class CompoundSourceHolder: public Outputs::Speaker::SampleSource { public: void get_samples(std::size_t number_of_samples, std::int16_t *target) { std::memset(target, 0, sizeof(std::int16_t) * number_of_samples); } void set_scaled_volume_range(int16_t range, float *volumes) {} std::size_t size() { return 0; } }; template class CompoundSourceHolder { public: CompoundSourceHolder(S &source, R &...next) : source_(source), next_source_(next...) {} void get_samples(std::size_t number_of_samples, std::int16_t *target) { if(source_.is_zero_level()) { source_.skip_samples(number_of_samples); next_source_.get_samples(number_of_samples, target); } else { int16_t next_samples[number_of_samples]; next_source_.get_samples(number_of_samples, next_samples); source_.get_samples(number_of_samples, target); while(number_of_samples--) { target[number_of_samples] += next_samples[number_of_samples]; } } } void skip_samples(const std::size_t number_of_samples) { source_.skip_samples(number_of_samples); next_source_.skip_samples(number_of_samples); } void set_scaled_volume_range(int16_t range, float *volumes) { source_.set_sample_volume_range(static_cast(static_cast(range * volumes[0]))); next_source_.set_scaled_volume_range(range, &volumes[1]); } std::size_t size() { return 1+next_source_.size(); } private: S &source_; CompoundSourceHolder next_source_; }; CompoundSourceHolder source_holder_; std::vector volumes_; int16_t volume_range_ = 0; }; } } #endif /* CompoundSource_h */