mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-11 15:49:38 +00:00
124 lines
3.4 KiB
C++
124 lines
3.4 KiB
C++
//
|
|
// 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 <cassert>
|
|
#include <cstring>
|
|
|
|
namespace Outputs {
|
|
namespace Speaker {
|
|
|
|
/*!
|
|
A CompoundSource adds together the sound generated by multiple individual SampleSources.
|
|
An owner may optionally assign relative volumes.
|
|
*/
|
|
template <typename... T> 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<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_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<float> &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 <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;
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
#endif /* CompoundSource_h */
|