diff --git a/Components/KonamiSCC/KonamiSCC.hpp b/Components/KonamiSCC/KonamiSCC.hpp index 21828ed5a..e524614d4 100644 --- a/Components/KonamiSCC/KonamiSCC.hpp +++ b/Components/KonamiSCC/KonamiSCC.hpp @@ -32,6 +32,7 @@ class SCC: public ::Outputs::Speaker::SampleSource { /// As per ::SampleSource; provides audio output. void get_samples(std::size_t number_of_samples, std::int16_t *target); void set_sample_volume_range(std::int16_t range); + static constexpr bool get_is_stereo() { return false; } /// Writes to the SCC. void write(uint16_t address, uint8_t value); diff --git a/Outputs/Speaker/Implementation/CompoundSource.hpp b/Outputs/Speaker/Implementation/CompoundSource.hpp index 8d9032e15..8a6c8f1d9 100644 --- a/Outputs/Speaker/Implementation/CompoundSource.hpp +++ b/Outputs/Speaker/Implementation/CompoundSource.hpp @@ -33,7 +33,7 @@ template class CompoundSource: } void get_samples(std::size_t number_of_samples, std::int16_t *target) { - source_holder_.get_samples(number_of_samples, target); + source_holder_.template get_samples(number_of_samples, target); } void skip_samples(const std::size_t number_of_samples) { @@ -66,7 +66,7 @@ template class CompoundSource: template class CompoundSourceHolder: public Outputs::Speaker::SampleSource { public: - void get_samples(std::size_t number_of_samples, std::int16_t *target) { + template void get_samples(std::size_t number_of_samples, std::int16_t *target) { std::memset(target, 0, sizeof(std::int16_t) * number_of_samples); } @@ -85,18 +85,39 @@ template class CompoundSource: public: CompoundSourceHolder(S &source, R &...next) : source_(source), next_source_(next...) {} - void get_samples(std::size_t number_of_samples, std::int16_t *target) { + template void get_samples(std::size_t number_of_samples, std::int16_t *target) { + // Get the rest of the output. + next_source_.template get_samples(number_of_samples, target); + if(source_.is_zero_level()) { + // This component is currently outputting silence; therefore don't add anything to the output + // audio — just pass the call onward. source_.skip_samples(number_of_samples); - next_source_.get_samples(number_of_samples, target); + return; + } + + // Get this component's output. + auto buffer_size = number_of_samples * (output_stereo ? 2 : 1); + int16_t local_samples[buffer_size]; + source_.get_samples(number_of_samples, local_samples); + + // Merge it in; furthermore if total output is stereo but this source isn't, + // map it to stereo. + if constexpr (output_stereo == S::get_is_stereo()) { + while(buffer_size--) { + target[buffer_size] += local_samples[buffer_size]; + } } 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]; + // This will happen only if mapping from mono to stereo, never in the + // other direction, because the compound source outputs stereo if any + // subcomponent does. So it outputs mono only if no stereo devices are + // in the mixing chain. + while(buffer_size--) { + target[buffer_size] += local_samples[buffer_size >> 1]; } } + + // TODO: accelerate above? } void skip_samples(const std::size_t number_of_samples) {