1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

Enhances the CompoundSource so that constituents can have different volumes.

This commit is contained in:
Thomas Harte 2018-04-07 14:30:02 -04:00
parent 0bab7c88f0
commit 10f637d2cf
2 changed files with 85 additions and 45 deletions

View File

@ -144,6 +144,9 @@ class ConcreteMachine:
ay_.set_port_handler(&ay_port_handler_); ay_.set_port_handler(&ay_port_handler_);
speaker_.set_input_rate(3579545.0f / 2.0f); speaker_.set_input_rate(3579545.0f / 2.0f);
tape_player_.set_sleep_observer(this); 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() { ~ConcreteMachine() {

View File

@ -11,6 +11,7 @@
#include "SampleSource.hpp" #include "SampleSource.hpp"
#include <cassert>
#include <cstring> #include <cstring>
namespace Outputs { namespace Outputs {
@ -18,66 +19,102 @@ namespace Speaker {
/*! /*!
A CompoundSource adds together the sound generated by multiple individual SampleSources. 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 <typename... T> class CompoundSource: public Outputs::Speaker::SampleSource { template <typename... T> class CompoundSource:
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 <typename T, typename... R> class CompoundSource<T, R...>:
public Outputs::Speaker::SampleSource { public Outputs::Speaker::SampleSource {
public: public:
CompoundSource(T &source, R &...next) : source_(source), next_source_(next...) {} CompoundSource(T &... sources) : source_holder_(sources...) {
// Default: give all sources equal volume.
void get_samples(std::size_t number_of_samples, std::int16_t *target) { const float volume = 1.0f / static_cast<float>(source_holder_.size());
if(source_.is_zero_level()) { for(std::size_t c = 0; c < source_holder_.size(); ++c) {
source_.skip_samples(number_of_samples); volumes_.push_back(volume);
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 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) { void skip_samples(const std::size_t number_of_samples) {
source_.skip_samples(number_of_samples); source_holder_.skip_samples(number_of_samples);
next_source_.skip_samples(number_of_samples);
} }
void set_sample_volume_range(int16_t range) { 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); Sets the relative volumes of the various sources underlying this
next_source_.set_scaled_volume_range(range); 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.
*/
int size() { void set_relative_volumes(const std::vector<float> &volumes) {
return 1+next_source_.size(); assert(volumes.size() == source_holder_.size());
volumes_ = volumes;
push_volumes();
} }
private: private:
bool is_sleeping_ = false; void push_volumes() {
T &source_; source_holder_.set_scaled_volume_range(volume_range_, volumes_.data());
CompoundSource<R...> next_source_; }
template <typename... S> 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 <typename S, typename... R> class CompoundSourceHolder<S, R...> {
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<int16_t>(static_cast<float>(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<R...> next_source_;
};
CompoundSourceHolder<T...> source_holder_;
std::vector<float> volumes_;
int16_t volume_range_ = 0;
}; };
} }