From 50d356be2fcf9005917aa37d37f4282307ea6159 Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Sun, 16 Feb 2020 18:31:45 -0500
Subject: [PATCH] Ensures all audio sources, including compound sources,
 announce whether they're stereo correctly.

---
 Components/AudioToggle/AudioToggle.hpp            |  1 +
 Components/SN76489/SN76489.hpp                    |  1 +
 Machines/AmstradCPC/AmstradCPC.cpp                |  8 ++++----
 Machines/Apple/AppleII/AppleII.cpp                |  2 +-
 Machines/Atari/2600/Bus.hpp                       |  2 +-
 Machines/Atari/2600/TIASound.hpp                  |  1 +
 Machines/Atari/ST/AtariST.cpp                     |  4 ++--
 Machines/ColecoVision/ColecoVision.cpp            |  6 +++---
 Machines/Electron/Electron.cpp                    |  2 +-
 Machines/Electron/SoundGenerator.hpp              |  1 +
 Machines/MSX/MSX.cpp                              |  6 +++---
 Machines/MasterSystem/MasterSystem.cpp            |  2 +-
 Outputs/Speaker/Implementation/CompoundSource.hpp | 10 ++++++++++
 13 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/Components/AudioToggle/AudioToggle.hpp b/Components/AudioToggle/AudioToggle.hpp
index cd9d0e483..c009fe781 100644
--- a/Components/AudioToggle/AudioToggle.hpp
+++ b/Components/AudioToggle/AudioToggle.hpp
@@ -24,6 +24,7 @@ class Toggle: public Outputs::Speaker::SampleSource {
 		void get_samples(std::size_t number_of_samples, std::int16_t *target);
 		void set_sample_volume_range(std::int16_t range);
 		void skip_samples(const std::size_t number_of_samples);
+		static constexpr bool get_is_stereo() { return false; }
 
 		void set_output(bool enabled);
 		bool get_output();
diff --git a/Components/SN76489/SN76489.hpp b/Components/SN76489/SN76489.hpp
index dbc2e5cab..eafac818a 100644
--- a/Components/SN76489/SN76489.hpp
+++ b/Components/SN76489/SN76489.hpp
@@ -32,6 +32,7 @@ class SN76489: public Outputs::Speaker::SampleSource {
 		void get_samples(std::size_t number_of_samples, std::int16_t *target);
 		bool is_zero_level();
 		void set_sample_volume_range(std::int16_t range);
+		static constexpr bool get_is_stereo() { return false; }
 
 	private:
 		int master_divider_ = 0;
diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp
index a53e0d3b9..4a8ff2677 100644
--- a/Machines/AmstradCPC/AmstradCPC.cpp
+++ b/Machines/AmstradCPC/AmstradCPC.cpp
@@ -128,7 +128,7 @@ class AYDeferrer {
 			speaker_.set_input_rate(1000000);
 			// Per the CPC Wiki:
 			// "A is output to the right, channel C is output left, and channel B is output to both left and right".
-			ay_.set_output_mixing(true, 0.0, 0.5, 1.0, 1.0, 0.5, 0.0);
+			ay_.set_output_mixing(0.0, 0.5, 1.0, 1.0, 0.5, 0.0);
 		}
 
 		~AYDeferrer() {
@@ -156,14 +156,14 @@ class AYDeferrer {
 		}
 
 		/// @returns the AY itself.
-		GI::AY38910::AY38910 &ay() {
+		GI::AY38910::AY38910<true> &ay() {
 			return ay_;
 		}
 
 	private:
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
-		GI::AY38910::AY38910 ay_;
-		Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910, true> speaker_;
+		GI::AY38910::AY38910<true> ay_;
+		Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910<true>> speaker_;
 		HalfCycles cycles_since_update_;
 };
 
diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp
index 397d24afb..90f53048a 100644
--- a/Machines/Apple/AppleII/AppleII.cpp
+++ b/Machines/Apple/AppleII/AppleII.cpp
@@ -108,7 +108,7 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
 
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
 		Audio::Toggle audio_toggle_;
-		Outputs::Speaker::LowpassSpeaker<Audio::Toggle, false> speaker_;
+		Outputs::Speaker::LowpassSpeaker<Audio::Toggle> speaker_;
 		Cycles cycles_since_audio_update_;
 
 		// MARK: - Cards
diff --git a/Machines/Atari/2600/Bus.hpp b/Machines/Atari/2600/Bus.hpp
index 36c8a16b1..4b21b4a0b 100644
--- a/Machines/Atari/2600/Bus.hpp
+++ b/Machines/Atari/2600/Bus.hpp
@@ -41,7 +41,7 @@ class Bus {
 
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
 		TIASound tia_sound_;
-		Outputs::Speaker::LowpassSpeaker<TIASound, false> speaker_;
+		Outputs::Speaker::LowpassSpeaker<TIASound> speaker_;
 
 		// joystick state
 		uint8_t tia_input_value_[2] = {0xff, 0xff};
diff --git a/Machines/Atari/2600/TIASound.hpp b/Machines/Atari/2600/TIASound.hpp
index ec41e40de..8bc84cc2a 100644
--- a/Machines/Atari/2600/TIASound.hpp
+++ b/Machines/Atari/2600/TIASound.hpp
@@ -29,6 +29,7 @@ class TIASound: public Outputs::Speaker::SampleSource {
 		// To satisfy ::SampleSource.
 		void get_samples(std::size_t number_of_samples, int16_t *target);
 		void set_sample_volume_range(std::int16_t range);
+		static constexpr bool get_is_stereo() { return false; }
 
 	private:
 		Concurrency::DeferringAsyncTaskQueue &audio_queue_;
diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp
index a4791e412..c8056e9e1 100644
--- a/Machines/Atari/ST/AtariST.cpp
+++ b/Machines/Atari/ST/AtariST.cpp
@@ -497,8 +497,8 @@ class ConcreteMachine:
 		JustInTimeActor<Motorola::ACIA::ACIA, 16> midi_acia_;
 
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
-		GI::AY38910::AY38910 ay_;
-		Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910, false> speaker_;
+		GI::AY38910::AY38910<false> ay_;
+		Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910<false>> speaker_;
 		HalfCycles cycles_since_audio_update_;
 
 		JustInTimeActor<DMAController> dma_;
diff --git a/Machines/ColecoVision/ColecoVision.cpp b/Machines/ColecoVision/ColecoVision.cpp
index 3c02ec646..02aacc6df 100644
--- a/Machines/ColecoVision/ColecoVision.cpp
+++ b/Machines/ColecoVision/ColecoVision.cpp
@@ -405,9 +405,9 @@ class ConcreteMachine:
 
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
 		TI::SN76489 sn76489_;
-		GI::AY38910::AY38910 ay_;
-		Outputs::Speaker::CompoundSource<TI::SN76489, GI::AY38910::AY38910> mixer_;
-		Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<TI::SN76489, GI::AY38910::AY38910>, false> speaker_;
+		GI::AY38910::AY38910<false> ay_;
+		Outputs::Speaker::CompoundSource<TI::SN76489, GI::AY38910::AY38910<false>> mixer_;
+		Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<TI::SN76489, GI::AY38910::AY38910<false>>> speaker_;
 
 		std::vector<uint8_t> bios_;
 		std::vector<uint8_t> cartridge_;
diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp
index fc89a1f37..a5c890207 100644
--- a/Machines/Electron/Electron.cpp
+++ b/Machines/Electron/Electron.cpp
@@ -592,7 +592,7 @@ class ConcreteMachine:
 
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
 		SoundGenerator sound_generator_;
-		Outputs::Speaker::LowpassSpeaker<SoundGenerator, false> speaker_;
+		Outputs::Speaker::LowpassSpeaker<SoundGenerator> speaker_;
 
 		bool speaker_is_enabled_ = false;
 
diff --git a/Machines/Electron/SoundGenerator.hpp b/Machines/Electron/SoundGenerator.hpp
index 9df5769da..fff2ba15c 100644
--- a/Machines/Electron/SoundGenerator.hpp
+++ b/Machines/Electron/SoundGenerator.hpp
@@ -28,6 +28,7 @@ class SoundGenerator: public ::Outputs::Speaker::SampleSource {
 		void get_samples(std::size_t number_of_samples, int16_t *target);
 		void skip_samples(std::size_t number_of_samples);
 		void set_sample_volume_range(std::int16_t range);
+		static constexpr bool get_is_stereo() { return false; }
 
 	private:
 		Concurrency::DeferringAsyncTaskQueue &audio_queue_;
diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp
index fd46acdb9..150f9eeb5 100644
--- a/Machines/MSX/MSX.cpp
+++ b/Machines/MSX/MSX.cpp
@@ -760,11 +760,11 @@ class ConcreteMachine:
 		Intel::i8255::i8255<i8255PortHandler> i8255_;
 
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
-		GI::AY38910::AY38910 ay_;
+		GI::AY38910::AY38910<false> ay_;
 		Audio::Toggle audio_toggle_;
 		Konami::SCC scc_;
-		Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, Audio::Toggle, Konami::SCC> mixer_;
-		Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, Audio::Toggle, Konami::SCC>, false> speaker_;
+		Outputs::Speaker::CompoundSource<GI::AY38910::AY38910<false>, Audio::Toggle, Konami::SCC> mixer_;
+		Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<GI::AY38910::AY38910<false>, Audio::Toggle, Konami::SCC>> speaker_;
 
 		Storage::Tape::BinaryTapePlayer tape_player_;
 		bool tape_player_is_sleeping_ = false;
diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp
index e7cb76f14..ce98a0bf8 100644
--- a/Machines/MasterSystem/MasterSystem.cpp
+++ b/Machines/MasterSystem/MasterSystem.cpp
@@ -424,7 +424,7 @@ class ConcreteMachine:
 
 		Concurrency::DeferringAsyncTaskQueue audio_queue_;
 		TI::SN76489 sn76489_;
-		Outputs::Speaker::LowpassSpeaker<TI::SN76489, false> speaker_;
+		Outputs::Speaker::LowpassSpeaker<TI::SN76489> speaker_;
 
 		std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
 		Inputs::Keyboard keyboard_;
diff --git a/Outputs/Speaker/Implementation/CompoundSource.hpp b/Outputs/Speaker/Implementation/CompoundSource.hpp
index bafed9986..8d9032e15 100644
--- a/Outputs/Speaker/Implementation/CompoundSource.hpp
+++ b/Outputs/Speaker/Implementation/CompoundSource.hpp
@@ -57,6 +57,8 @@ template <typename... T> class CompoundSource:
 			push_volumes();
 		}
 
+		static constexpr bool get_is_stereo() { return CompoundSourceHolder<T...>::get_is_stereo(); }
+
 	private:
 		void push_volumes() {
 			source_holder_.set_scaled_volume_range(volume_range_, volumes_.data());
@@ -73,6 +75,10 @@ template <typename... T> class CompoundSource:
 				std::size_t size() {
 					return 0;
 				}
+
+				static constexpr bool get_is_stereo() {
+					return false;
+				}
 		};
 
 		template <typename S, typename... R> class CompoundSourceHolder<S, R...> {
@@ -107,6 +113,10 @@ template <typename... T> class CompoundSource:
 					return 1+next_source_.size();
 				}
 
+				static constexpr bool get_is_stereo() {
+					return S::get_is_stereo() || CompoundSourceHolder<R...>::get_is_stereo();
+				}
+
 			private:
 				S &source_;
 				CompoundSourceHolder<R...> next_source_;