1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-03 11:30:02 +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_);
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() {

View File

@ -11,6 +11,7 @@
#include "SampleSource.hpp"
#include <cassert>
#include <cstring>
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 <typename... T> 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 <typename T, typename... R> class CompoundSource<T, R...>:
template <typename... T> 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<float>(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<float> &volumes) {
assert(volumes.size() == source_holder_.size());
volumes_ = volumes;
push_volumes();
}
private:
bool is_sleeping_ = false;
T &source_;
CompoundSource<R...> next_source_;
void push_volumes() {
source_holder_.set_scaled_volume_range(volume_range_, volumes_.data());
}
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;
};
}