diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index 0995c2545..24d8e5479 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -17,7 +17,7 @@ namespace MOS::MOS6560 { // audio state -class AudioGenerator: public ::Outputs::Speaker::SampleSource { +class AudioGenerator: public Outputs::Speaker::SampleSource { public: AudioGenerator(Concurrency::AsyncTaskQueue &audio_queue); @@ -25,7 +25,7 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource void AY38910::set_output_mixing(float a_lef c_right_ = uint8_t(c_right * 255.0f); } -template void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) { +template +void AY38910::get_samples( + std::size_t number_of_samples, + typename Outputs::Speaker::SampleT::type *target +) { // Note on structure below: the real AY has a built-in divider of 8 // prior to applying its tone and noise dividers. But the YM fills the // same total periods for noise and tone with double-precision envelopes. @@ -113,11 +117,7 @@ template void AY38910::get_samples(std::size_t numbe std::size_t c = 0; while((master_divider_&3) && c < number_of_samples) { - if constexpr (is_stereo) { - reinterpret_cast(target)[c] = output_volume_; - } else { - target[c] = int16_t(output_volume_); - } + target[c] = output_volume_; master_divider_++; c++; } @@ -159,11 +159,7 @@ template void AY38910::get_samples(std::size_t numbe evaluate_output_volume(); for(int ic = 0; ic < 4 && c < number_of_samples; ic++) { - if constexpr (is_stereo) { - reinterpret_cast(target)[c] = output_volume_; - } else { - target[c] = int16_t(output_volume_); - } + target[c] = output_volume_; c++; master_divider_++; } @@ -214,19 +210,19 @@ template void AY38910::evaluate_output_volume() { // Mix additively, weighting if in stereo. if constexpr (is_stereo) { - int16_t *const output_volumes = reinterpret_cast(&output_volume_); - output_volumes[0] = int16_t(( +// int16_t *const output_volumes = reinterpret_cast(&output_volume_); + output_volume_[0] = int16_t(( volumes_[volumes[0]] * channel_levels[0] * a_left_ + volumes_[volumes[1]] * channel_levels[1] * b_left_ + volumes_[volumes[2]] * channel_levels[2] * c_left_ ) >> 8); - output_volumes[1] = int16_t(( + output_volume_[1] = int16_t(( volumes_[volumes[0]] * channel_levels[0] * a_right_ + volumes_[volumes[1]] * channel_levels[1] * b_right_ + volumes_[volumes[2]] * channel_levels[2] * c_right_ ) >> 8); } else { - output_volume_ = uint32_t( + output_volume_ = int16_t( volumes_[volumes[0]] * channel_levels[0] + volumes_[volumes[1]] * channel_levels[1] + volumes_[volumes[2]] * channel_levels[2] diff --git a/Components/AY38910/AY38910.hpp b/Components/AY38910/AY38910.hpp index f9ffe1476..58eb4ec80 100644 --- a/Components/AY38910/AY38910.hpp +++ b/Components/AY38910/AY38910.hpp @@ -106,7 +106,7 @@ template class AY38910: public ::Outputs::Speaker::SampleSource::type *target); bool is_zero_level() const; void set_sample_volume_range(std::int16_t range); @@ -149,7 +149,7 @@ template class AY38910: public ::Outputs::Speaker::SampleSource::type output_volume_; void update_bus(); PortHandler *port_handler_ = nullptr; diff --git a/Components/AudioToggle/AudioToggle.hpp b/Components/AudioToggle/AudioToggle.hpp index 26ef7007e..b93ea4cae 100644 --- a/Components/AudioToggle/AudioToggle.hpp +++ b/Components/AudioToggle/AudioToggle.hpp @@ -20,7 +20,7 @@ class Toggle: public Outputs::Speaker::SampleSource { public: Toggle(Concurrency::AsyncTaskQueue &audio_queue); - void get_samples(std::size_t number_of_samples, std::int16_t *target); + void get_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target); void set_sample_volume_range(std::int16_t range); void skip_samples(const std::size_t number_of_samples); diff --git a/Machines/Enterprise/Dave.cpp b/Machines/Enterprise/Dave.cpp index e619f6352..940755014 100644 --- a/Machines/Enterprise/Dave.cpp +++ b/Machines/Enterprise/Dave.cpp @@ -99,17 +99,14 @@ void Audio::update_channel(int c) { channels_[c].output |= output; } -void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { - struct Frame { - int16_t left, right; - } output_level; - Frame *target_frames = reinterpret_cast(target); +void Audio::get_samples(std::size_t number_of_samples, Outputs::Speaker::StereoSample *target) { + Outputs::Speaker::StereoSample output_level; size_t c = 0; while(c < number_of_samples) { // I'm unclear on the details of the time division multiplexing so, // for now, just sum the outputs. - output_level.left = + output_level[0] = volume_ * (use_direct_output_[0] ? channels_[0].amplitude[0] @@ -120,7 +117,7 @@ void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { noise_.amplitude[0] * noise_.final_output )); - output_level.right = + output_level[1] = volume_ * (use_direct_output_[1] ? channels_[0].amplitude[1] @@ -133,14 +130,11 @@ void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { while(global_divider_ && c < number_of_samples) { --global_divider_; - target_frames[c] = output_level; + target[c] = output_level; ++c; } - global_divider_ = global_divider_reload_; - if(!global_divider_) { - global_divider_ = global_divider_reload_; - } + poly_state_[int(Channel::Distortion::FourBit)] = poly4_.next(); poly_state_[int(Channel::Distortion::FiveBit)] = poly5_.next(); poly_state_[int(Channel::Distortion::SevenBit)] = poly7_.next(); diff --git a/Machines/Enterprise/Dave.hpp b/Machines/Enterprise/Dave.hpp index aad6423b6..a3e369251 100644 --- a/Machines/Enterprise/Dave.hpp +++ b/Machines/Enterprise/Dave.hpp @@ -37,7 +37,7 @@ class Audio: public Outputs::Speaker::SampleSource { // MARK: - SampleSource. void set_sample_volume_range(int16_t range); - void get_samples(std::size_t number_of_samples, int16_t *target); + void get_samples(std::size_t number_of_samples, Outputs::Speaker::StereoSample *target); private: Concurrency::AsyncTaskQueue &audio_queue_; diff --git a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp index 3b64dfcc6..5634de15d 100644 --- a/Outputs/Speaker/Implementation/LowpassSpeaker.hpp +++ b/Outputs/Speaker/Implementation/LowpassSpeaker.hpp @@ -404,7 +404,12 @@ template class PullLowpass: public LowpassBase(target); + sample_source_.get_samples(length, stereo_target); + } else { + sample_source_.get_samples(length, target); + } } }; diff --git a/Outputs/Speaker/Implementation/SampleSource.hpp b/Outputs/Speaker/Implementation/SampleSource.hpp index 5353db641..36afe40f7 100644 --- a/Outputs/Speaker/Implementation/SampleSource.hpp +++ b/Outputs/Speaker/Implementation/SampleSource.hpp @@ -12,11 +12,9 @@ #include #include -namespace Outputs::Speaker { +#include "../Speaker.hpp" -template struct SampleT; -template <> struct SampleT { using type = std::array; }; -template <> struct SampleT { using type = std::int16_t; }; +namespace Outputs::Speaker { /*! A sample source is something that can provide a stream of audio. @@ -34,18 +32,11 @@ class SampleSource { /*! Should write the next @c number_of_samples to @c target. */ - void get_samples(std::size_t number_of_samples, std::int16_t *target) { + void get_samples(std::size_t number_of_samples, typename SampleT::type *target) { const auto &source = *static_cast(this); while(number_of_samples--) { - if constexpr (is_stereo) { - const auto next = source.level(); - target[0] = next[0]; - target[1] = next[1]; - target += 2; - } else { - *target = source.level(); - ++target; - } + *target = source.level(); + ++target; } } @@ -59,7 +50,7 @@ class SampleSource { if constexpr (&SourceT::advance == &SampleSource::advance) { return; } - std::int16_t scratch_pad[number_of_samples]; + typename SampleT::type scratch_pad[number_of_samples]; get_samples(number_of_samples, scratch_pad); } @@ -68,16 +59,8 @@ class SampleSource { fill the target with zeroes; @c false if a call might return all zeroes or might not. */ - bool is_zero_level() const { return false; } - auto level() const { - typename SampleT::type result; - if constexpr (is_stereo) { - result[0] = result[1] = 0; - } else { - result = 0; - } - return result; - } + bool is_zero_level() const { return false; } + auto level() const { return typename SampleT::type(); } void advance() {} /*! diff --git a/Outputs/Speaker/Speaker.hpp b/Outputs/Speaker/Speaker.hpp index c35b182c1..d65a54fc4 100644 --- a/Outputs/Speaker/Speaker.hpp +++ b/Outputs/Speaker/Speaker.hpp @@ -14,6 +14,14 @@ namespace Outputs::Speaker { +template struct SampleT; +template <> struct SampleT { using type = std::array; }; // TODO: adopt left/right as per Dave? +template <> struct SampleT { using type = std::int16_t; }; + +// Shorthands. +using MonoSample = SampleT::type; +using StereoSample = SampleT::type; + /*! Provides a communication point for sound; machines that have a speaker provide an audio output.