1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-26 10:29:31 +00:00

Adds mixdown/up capability to Speaker.

To deal with occasions when the host machine just always is either mono or stereo, and the emulated machine must cope.
This commit is contained in:
Thomas Harte 2020-02-16 13:50:18 -05:00
parent ea1c8a3b81
commit 90856a0e7a
5 changed files with 34 additions and 7 deletions

View File

@ -38,6 +38,7 @@ float MultiSpeaker::get_ideal_clock_rate_in_range(float minimum, float maximum)
}
void MultiSpeaker::set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) {
is_stereo_ = stereo;
for(const auto &speaker: speakers_) {
speaker->set_computed_output_rate(cycles_per_second, buffer_size, stereo);
}
@ -53,7 +54,7 @@ void MultiSpeaker::speaker_did_complete_samples(Speaker *speaker, const std::vec
std::lock_guard<std::mutex> lock_guard(front_speaker_mutex_);
if(speaker != front_speaker_) return;
}
did_complete_samples(this, buffer);
did_complete_samples(this, buffer, is_stereo_);
}
void MultiSpeaker::speaker_did_change_input_clock(Speaker *speaker) {

View File

@ -51,6 +51,8 @@ class MultiSpeaker: public Outputs::Speaker::Speaker, Outputs::Speaker::Speaker:
Outputs::Speaker::Speaker *front_speaker_ = nullptr;
Outputs::Speaker::Speaker::Delegate *delegate_ = nullptr;
std::mutex front_speaker_mutex_;
bool is_stereo_ = false;
};
}

View File

@ -58,11 +58,11 @@
</CommandLineArgument>
<CommandLineArgument
argument = "&quot;/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Master System/R-Type (NTSC).sms&quot;"
isEnabled = "NO">
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "&quot;/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Amstrad CPC/Robocop.dsk&quot;"
isEnabled = "YES">
isEnabled = "NO">
</CommandLineArgument>
<CommandLineArgument
argument = "--speed=5"

View File

@ -151,7 +151,7 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
// Announce to delegate if full.
if(output_buffer_pointer_ == output_buffer_.size()) {
output_buffer_pointer_ = 0;
did_complete_samples(this, output_buffer_);
did_complete_samples(this, output_buffer_, is_stereo);
}
cycles_remaining -= cycles_to_read;
@ -273,7 +273,7 @@ template <typename SampleSource, bool is_stereo> class LowpassSpeaker: public Sp
// Announce to delegate if full.
if(output_buffer_pointer_ == output_buffer_.size()) {
output_buffer_pointer_ = 0;
did_complete_samples(this, output_buffer_);
did_complete_samples(this, output_buffer_, is_stereo);
}
// If the next loop around is going to reuse some of the samples just collected, use a memmove to

View File

@ -81,9 +81,32 @@ class Speaker {
virtual void set_computed_output_rate(float cycles_per_second, int buffer_size, bool stereo) = 0;
protected:
void did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer) {
void did_complete_samples(Speaker *speaker, const std::vector<int16_t> &buffer, bool is_stereo) {
++completed_sample_sets_;
delegate_->speaker_did_complete_samples(this, buffer);
// Hope for the fast path first: producer and consumer agree about
// number of channels.
if(is_stereo == stereo_output_) {
delegate_->speaker_did_complete_samples(this, buffer);
return;
}
// Producer and consumer don't agree, so mix two channels to one, or double out one to two.
if(is_stereo) {
// Mix down.
mix_buffer_.resize(buffer.size() / 2);
for(size_t c = 0; c < mix_buffer_.size(); ++c) {
mix_buffer_[c] = (buffer[(c << 1) + 0] + buffer[(c << 1) + 1]) >> 1;
// TODO: is there an Accelerate framework solution to this?
}
} else {
// Double up.
mix_buffer_.resize(buffer.size() * 2);
for(size_t c = 0; c < buffer.size(); ++c) {
mix_buffer_[(c << 1) + 0] = mix_buffer_[(c << 1) + 1] = buffer[c];
}
}
delegate_->speaker_did_complete_samples(this, mix_buffer_);
}
Delegate *delegate_ = nullptr;
@ -99,6 +122,7 @@ class Speaker {
float output_cycles_per_second_ = 1.0f;
int output_buffer_size_ = 1;
bool stereo_output_ = false;
std::vector<int16_t> mix_buffer_;
};
}