diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 3502c8161..35807af48 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -144,6 +144,9 @@ class ConcreteMachine: ay_.set_port_handler(&ay_port_handler_); speaker_.set_input_rate(3579545.0f / 2.0f); tape_player_.set_sleep_observer(this); + + // Set the AY to 50% of available volume, the toggle to 10% and leave 40% for an SCC. + mixer_.set_relative_volumes({0.5f, 0.1f, 0.4f}); } ~ConcreteMachine() { diff --git a/Outputs/Speaker/Implementation/CompoundSource.hpp b/Outputs/Speaker/Implementation/CompoundSource.hpp index b082ad671..d8232cb7c 100644 --- a/Outputs/Speaker/Implementation/CompoundSource.hpp +++ b/Outputs/Speaker/Implementation/CompoundSource.hpp @@ -11,6 +11,7 @@ #include "SampleSource.hpp" +#include #include namespace Outputs { @@ -18,66 +19,102 @@ namespace Speaker { /*! A CompoundSource adds together the sound generated by multiple individual SampleSources. - It's an instance of template metaprogramming; this is the base case. + An owner may optionally assign relative volumes. */ -template class CompoundSource: 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) {} - - int size() { - return 0; - } -}; - -/*! - A CompoundSource adds together the sound generated by multiple individual SampleSources. - It's an instance of template metaprogramming; this is the recursive case. -*/ -template class CompoundSource: +template class CompoundSource: public Outputs::Speaker::SampleSource { public: - CompoundSource(T &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]; - } + 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_.skip_samples(number_of_samples); - next_source_.skip_samples(number_of_samples); + source_holder_.skip_samples(number_of_samples); } void set_sample_volume_range(int16_t range) { - set_scaled_volume_range(range / size()); + volume_range_ = range; + push_volumes(); + source_holder_.set_scaled_volume_range(range, volumes_.data()); } - void set_scaled_volume_range(int16_t range) { - source_.set_sample_volume_range(range); - next_source_.set_scaled_volume_range(range); - } - - int size() { - return 1+next_source_.size(); + /*! + 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: - bool is_sleeping_ = false; - T &source_; - CompoundSource next_source_; + 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; }; }