mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Merge pull request #408 from TomHarte/MixerBalance
Enhances the CompoundSource so that constituents can have different volumes.
This commit is contained in:
commit
506b4da6c3
@ -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() {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user