mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-24 12:30:17 +00:00
Makes some attempt at stereo support, with the Amstrad CPC being the test case.
This commit is contained in:
parent
89d6b85b83
commit
e66a3523b6
@ -126,6 +126,7 @@ class AYDeferrer {
|
||||
/// Constructs a new AY instance and sets its clock rate.
|
||||
AYDeferrer() : ay_(GI::AY38910::Personality::AY38910, audio_queue_), speaker_(ay_) {
|
||||
speaker_.set_input_rate(1000000);
|
||||
ay_.set_output_mixing(true, 0.0, 0.5, 1.0, 1.0, 0.5, 0.0);
|
||||
}
|
||||
|
||||
~AYDeferrer() {
|
||||
@ -160,7 +161,7 @@ class AYDeferrer {
|
||||
private:
|
||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||
GI::AY38910::AY38910 ay_;
|
||||
Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910, false> speaker_;
|
||||
Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910, true> speaker_;
|
||||
HalfCycles cycles_since_update_;
|
||||
};
|
||||
|
||||
|
@ -66,7 +66,7 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
||||
|
||||
filter_parameters_.output_cycles_per_second = cycles_per_second;
|
||||
filter_parameters_.parameters_are_dirty = true;
|
||||
output_buffer_.resize(std::size_t(buffer_size));
|
||||
output_buffer_.resize(std::size_t(buffer_size) * (is_stereo ? 2 : 1);
|
||||
}
|
||||
|
||||
bool get_is_stereo() final {
|
||||
@ -144,12 +144,11 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
||||
switch(conversion_) {
|
||||
case Conversion::Copy:
|
||||
while(cycles_remaining) {
|
||||
const auto cycles_to_read = std::min(output_buffer_.size() - output_buffer_pointer_, cycles_remaining);
|
||||
const auto cycles_to_read = std::min((output_buffer_.size() - output_buffer_pointer_) / (is_stereo ? 2 : 1), cycles_remaining);
|
||||
sample_source_.get_samples(cycles_to_read, &output_buffer_[output_buffer_pointer_ ]);
|
||||
output_buffer_pointer_ += cycles_to_read * (is_stereo ? 2 : 1);
|
||||
|
||||
sample_source_.get_samples(cycles_to_read, &output_buffer_[output_buffer_pointer_]);
|
||||
output_buffer_pointer_ += cycles_to_read;
|
||||
|
||||
// announce to delegate if full
|
||||
// Announce to delegate if full.
|
||||
if(output_buffer_pointer_ == output_buffer_.size()) {
|
||||
output_buffer_pointer_ = 0;
|
||||
did_complete_samples(this, output_buffer_);
|
||||
@ -161,14 +160,16 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
||||
|
||||
case Conversion::ResampleSmaller:
|
||||
while(cycles_remaining) {
|
||||
const auto cycles_to_read = std::min(cycles_remaining, input_buffer_.size() - input_buffer_depth_);
|
||||
const auto cycles_to_read = std::min((input_buffer_.size() - input_buffer_depth_) / (is_stereo ? 2 : 1), cycles_remaining);
|
||||
|
||||
sample_source_.get_samples(cycles_to_read, &input_buffer_[input_buffer_depth_]);
|
||||
cycles_remaining -= cycles_to_read;
|
||||
input_buffer_depth_ += cycles_to_read;
|
||||
input_buffer_depth_ += cycles_to_read * (is_stereo ? 2 : 1);
|
||||
|
||||
if(input_buffer_depth_ == input_buffer_.size()) {
|
||||
resample_input_buffer();
|
||||
}
|
||||
|
||||
cycles_remaining -= cycles_to_read;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -243,24 +244,31 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
||||
// that means nothing to do.
|
||||
default: break;
|
||||
|
||||
case Conversion::ResampleSmaller:
|
||||
case Conversion::ResampleSmaller: {
|
||||
// Reize the input buffer only if absolutely necessary; if sizing downward
|
||||
// such that a sample would otherwise be lost then output it now. Keep anything
|
||||
// currently in the input buffer that hasn't yet been processed.
|
||||
if(input_buffer_.size() != size_t(number_of_taps)) {
|
||||
if(input_buffer_depth_ >= size_t(number_of_taps)) {
|
||||
const size_t required_buffer_size = size_t(number_of_taps) * (is_stereo ? 2 : 1);
|
||||
if(input_buffer_.size() != required_buffer_size) {
|
||||
if(input_buffer_depth_ >= required_buffer_size) {
|
||||
resample_input_buffer();
|
||||
input_buffer_depth_ %= size_t(number_of_taps);
|
||||
input_buffer_depth_ %= required_buffer_size;
|
||||
}
|
||||
input_buffer_.resize(size_t(number_of_taps));
|
||||
input_buffer_.resize(required_buffer_size);
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void resample_input_buffer() {
|
||||
output_buffer_[output_buffer_pointer_] = filter_->apply(input_buffer_.data());
|
||||
output_buffer_pointer_++;
|
||||
if constexpr (is_stereo) {
|
||||
output_buffer_[output_buffer_pointer_ + 0] = filter_->apply(input_buffer_.data(), 2);
|
||||
output_buffer_[output_buffer_pointer_ + 1] = filter_->apply(input_buffer_.data() + 1, 2);
|
||||
output_buffer_pointer_+= 2;
|
||||
} else {
|
||||
output_buffer_[output_buffer_pointer_] = filter_->apply(input_buffer_.data());
|
||||
output_buffer_pointer_++;
|
||||
}
|
||||
|
||||
// Announce to delegate if full.
|
||||
if(output_buffer_pointer_ == output_buffer_.size()) {
|
||||
@ -279,8 +287,9 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
||||
sizeof(int16_t) * (input_buffer_.size() - steps));
|
||||
input_buffer_depth_ -= steps;
|
||||
} else {
|
||||
if(steps > input_buffer_.size())
|
||||
sample_source_.skip_samples(steps - input_buffer_.size());
|
||||
if(steps > input_buffer_.size()) {
|
||||
sample_source_.skip_samples((steps - input_buffer_.size()) / (is_stereo ? 2 : 1));
|
||||
}
|
||||
input_buffer_depth_ = 0;
|
||||
}
|
||||
}
|
||||
|
@ -49,15 +49,15 @@ class FIRFilter {
|
||||
@param src The source buffer to apply the filter to.
|
||||
@returns The result of applying the filter.
|
||||
*/
|
||||
inline short apply(const short *src) const {
|
||||
inline short apply(const short *src, size_t stride = 1) const {
|
||||
#ifdef __APPLE__
|
||||
short result;
|
||||
vDSP_dotpr_s1_15(filter_coefficients_.data(), 1, src, 1, &result, filter_coefficients_.size());
|
||||
vDSP_dotpr_s1_15(filter_coefficients_.data(), 1, src, stride, &result, filter_coefficients_.size());
|
||||
return result;
|
||||
#else
|
||||
int outputValue = 0;
|
||||
for(std::size_t c = 0; c < filter_coefficients_.size(); ++c) {
|
||||
outputValue += filter_coefficients_[c] * src[c];
|
||||
outputValue += filter_coefficients_[c] * src[c * stride];
|
||||
}
|
||||
return static_cast<short>(outputValue >> FixedShift);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user