mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 03:32:01 +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.
|
/// Constructs a new AY instance and sets its clock rate.
|
||||||
AYDeferrer() : ay_(GI::AY38910::Personality::AY38910, audio_queue_), speaker_(ay_) {
|
AYDeferrer() : ay_(GI::AY38910::Personality::AY38910, audio_queue_), speaker_(ay_) {
|
||||||
speaker_.set_input_rate(1000000);
|
speaker_.set_input_rate(1000000);
|
||||||
|
ay_.set_output_mixing(true, 0.0, 0.5, 1.0, 1.0, 0.5, 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AYDeferrer() {
|
~AYDeferrer() {
|
||||||
@ -160,7 +161,7 @@ class AYDeferrer {
|
|||||||
private:
|
private:
|
||||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||||
GI::AY38910::AY38910 ay_;
|
GI::AY38910::AY38910 ay_;
|
||||||
Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910, false> speaker_;
|
Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910, true> speaker_;
|
||||||
HalfCycles cycles_since_update_;
|
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_.output_cycles_per_second = cycles_per_second;
|
||||||
filter_parameters_.parameters_are_dirty = true;
|
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 {
|
bool get_is_stereo() final {
|
||||||
@ -144,12 +144,11 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
|||||||
switch(conversion_) {
|
switch(conversion_) {
|
||||||
case Conversion::Copy:
|
case Conversion::Copy:
|
||||||
while(cycles_remaining) {
|
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_]);
|
// Announce to delegate if full.
|
||||||
output_buffer_pointer_ += cycles_to_read;
|
|
||||||
|
|
||||||
// announce to delegate if full
|
|
||||||
if(output_buffer_pointer_ == output_buffer_.size()) {
|
if(output_buffer_pointer_ == output_buffer_.size()) {
|
||||||
output_buffer_pointer_ = 0;
|
output_buffer_pointer_ = 0;
|
||||||
did_complete_samples(this, output_buffer_);
|
did_complete_samples(this, output_buffer_);
|
||||||
@ -161,14 +160,16 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
|||||||
|
|
||||||
case Conversion::ResampleSmaller:
|
case Conversion::ResampleSmaller:
|
||||||
while(cycles_remaining) {
|
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_]);
|
sample_source_.get_samples(cycles_to_read, &input_buffer_[input_buffer_depth_]);
|
||||||
cycles_remaining -= cycles_to_read;
|
input_buffer_depth_ += cycles_to_read * (is_stereo ? 2 : 1);
|
||||||
input_buffer_depth_ += cycles_to_read;
|
|
||||||
|
|
||||||
if(input_buffer_depth_ == input_buffer_.size()) {
|
if(input_buffer_depth_ == input_buffer_.size()) {
|
||||||
resample_input_buffer();
|
resample_input_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cycles_remaining -= cycles_to_read;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -243,24 +244,31 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
|
|||||||
// that means nothing to do.
|
// that means nothing to do.
|
||||||
default: break;
|
default: break;
|
||||||
|
|
||||||
case Conversion::ResampleSmaller:
|
case Conversion::ResampleSmaller: {
|
||||||
// Reize the input buffer only if absolutely necessary; if sizing downward
|
// 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
|
// 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.
|
// currently in the input buffer that hasn't yet been processed.
|
||||||
if(input_buffer_.size() != size_t(number_of_taps)) {
|
const size_t required_buffer_size = size_t(number_of_taps) * (is_stereo ? 2 : 1);
|
||||||
if(input_buffer_depth_ >= size_t(number_of_taps)) {
|
if(input_buffer_.size() != required_buffer_size) {
|
||||||
|
if(input_buffer_depth_ >= required_buffer_size) {
|
||||||
resample_input_buffer();
|
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() {
|
inline void resample_input_buffer() {
|
||||||
output_buffer_[output_buffer_pointer_] = filter_->apply(input_buffer_.data());
|
if constexpr (is_stereo) {
|
||||||
output_buffer_pointer_++;
|
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.
|
// Announce to delegate if full.
|
||||||
if(output_buffer_pointer_ == output_buffer_.size()) {
|
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));
|
sizeof(int16_t) * (input_buffer_.size() - steps));
|
||||||
input_buffer_depth_ -= steps;
|
input_buffer_depth_ -= steps;
|
||||||
} else {
|
} else {
|
||||||
if(steps > input_buffer_.size())
|
if(steps > input_buffer_.size()) {
|
||||||
sample_source_.skip_samples(steps - input_buffer_.size());
|
sample_source_.skip_samples((steps - input_buffer_.size()) / (is_stereo ? 2 : 1));
|
||||||
|
}
|
||||||
input_buffer_depth_ = 0;
|
input_buffer_depth_ = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,15 +49,15 @@ class FIRFilter {
|
|||||||
@param src The source buffer to apply the filter to.
|
@param src The source buffer to apply the filter to.
|
||||||
@returns The result of applying the filter.
|
@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__
|
#ifdef __APPLE__
|
||||||
short result;
|
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;
|
return result;
|
||||||
#else
|
#else
|
||||||
int outputValue = 0;
|
int outputValue = 0;
|
||||||
for(std::size_t c = 0; c < filter_coefficients_.size(); ++c) {
|
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);
|
return static_cast<short>(outputValue >> FixedShift);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user