1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-13 00:25:26 +00:00

Introduce [Mono/Stereo]Sample types.

This commit is contained in:
Thomas Harte
2024-02-09 09:15:48 -05:00
parent a4a983eb81
commit f3d0827d14
9 changed files with 45 additions and 59 deletions

View File

@@ -17,7 +17,7 @@
namespace MOS::MOS6560 { namespace MOS::MOS6560 {
// audio state // audio state
class AudioGenerator: public ::Outputs::Speaker::SampleSource<AudioGenerator, false> { class AudioGenerator: public Outputs::Speaker::SampleSource<AudioGenerator, false> {
public: public:
AudioGenerator(Concurrency::AsyncTaskQueue<false> &audio_queue); AudioGenerator(Concurrency::AsyncTaskQueue<false> &audio_queue);
@@ -25,7 +25,7 @@ class AudioGenerator: public ::Outputs::Speaker::SampleSource<AudioGenerator, fa
void set_control(int channel, uint8_t value); void set_control(int channel, uint8_t value);
// For ::SampleSource. // For ::SampleSource.
void get_samples(std::size_t number_of_samples, int16_t *target); void get_samples(std::size_t number_of_samples, Outputs::Speaker::MonoSample *target);
void skip_samples(std::size_t number_of_samples); void skip_samples(std::size_t number_of_samples);
void set_sample_volume_range(std::int16_t range); void set_sample_volume_range(std::int16_t range);

View File

@@ -101,7 +101,11 @@ template <bool is_stereo> void AY38910<is_stereo>::set_output_mixing(float a_lef
c_right_ = uint8_t(c_right * 255.0f); c_right_ = uint8_t(c_right * 255.0f);
} }
template <bool is_stereo> void AY38910<is_stereo>::get_samples(std::size_t number_of_samples, int16_t *target) { template <bool is_stereo>
void AY38910<is_stereo>::get_samples(
std::size_t number_of_samples,
typename Outputs::Speaker::SampleT<is_stereo>::type *target
) {
// Note on structure below: the real AY has a built-in divider of 8 // 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 // prior to applying its tone and noise dividers. But the YM fills the
// same total periods for noise and tone with double-precision envelopes. // same total periods for noise and tone with double-precision envelopes.
@@ -113,11 +117,7 @@ template <bool is_stereo> void AY38910<is_stereo>::get_samples(std::size_t numbe
std::size_t c = 0; std::size_t c = 0;
while((master_divider_&3) && c < number_of_samples) { while((master_divider_&3) && c < number_of_samples) {
if constexpr (is_stereo) { target[c] = output_volume_;
reinterpret_cast<uint32_t *>(target)[c] = output_volume_;
} else {
target[c] = int16_t(output_volume_);
}
master_divider_++; master_divider_++;
c++; c++;
} }
@@ -159,11 +159,7 @@ template <bool is_stereo> void AY38910<is_stereo>::get_samples(std::size_t numbe
evaluate_output_volume(); evaluate_output_volume();
for(int ic = 0; ic < 4 && c < number_of_samples; ic++) { for(int ic = 0; ic < 4 && c < number_of_samples; ic++) {
if constexpr (is_stereo) { target[c] = output_volume_;
reinterpret_cast<uint32_t *>(target)[c] = output_volume_;
} else {
target[c] = int16_t(output_volume_);
}
c++; c++;
master_divider_++; master_divider_++;
} }
@@ -214,19 +210,19 @@ template <bool is_stereo> void AY38910<is_stereo>::evaluate_output_volume() {
// Mix additively, weighting if in stereo. // Mix additively, weighting if in stereo.
if constexpr (is_stereo) { if constexpr (is_stereo) {
int16_t *const output_volumes = reinterpret_cast<int16_t *>(&output_volume_); // int16_t *const output_volumes = reinterpret_cast<int16_t *>(&output_volume_);
output_volumes[0] = int16_t(( output_volume_[0] = int16_t((
volumes_[volumes[0]] * channel_levels[0] * a_left_ + volumes_[volumes[0]] * channel_levels[0] * a_left_ +
volumes_[volumes[1]] * channel_levels[1] * b_left_ + volumes_[volumes[1]] * channel_levels[1] * b_left_ +
volumes_[volumes[2]] * channel_levels[2] * c_left_ volumes_[volumes[2]] * channel_levels[2] * c_left_
) >> 8); ) >> 8);
output_volumes[1] = int16_t(( output_volume_[1] = int16_t((
volumes_[volumes[0]] * channel_levels[0] * a_right_ + volumes_[volumes[0]] * channel_levels[0] * a_right_ +
volumes_[volumes[1]] * channel_levels[1] * b_right_ + volumes_[volumes[1]] * channel_levels[1] * b_right_ +
volumes_[volumes[2]] * channel_levels[2] * c_right_ volumes_[volumes[2]] * channel_levels[2] * c_right_
) >> 8); ) >> 8);
} else { } else {
output_volume_ = uint32_t( output_volume_ = int16_t(
volumes_[volumes[0]] * channel_levels[0] + volumes_[volumes[0]] * channel_levels[0] +
volumes_[volumes[1]] * channel_levels[1] + volumes_[volumes[1]] * channel_levels[1] +
volumes_[volumes[2]] * channel_levels[2] volumes_[volumes[2]] * channel_levels[2]

View File

@@ -106,7 +106,7 @@ template <bool stereo> class AY38910: public ::Outputs::Speaker::SampleSource<AY
void set_output_mixing(float a_left, float b_left, float c_left, float a_right = 1.0, float b_right = 1.0, float c_right = 1.0); void set_output_mixing(float a_left, float b_left, float c_left, float a_right = 1.0, float b_right = 1.0, float c_right = 1.0);
// to satisfy ::Outputs::Speaker (included via ::Outputs::Filter. // to satisfy ::Outputs::Speaker (included via ::Outputs::Filter.
void get_samples(std::size_t number_of_samples, int16_t *target); void get_samples(std::size_t number_of_samples, typename Outputs::Speaker::SampleT<stereo>::type *target);
bool is_zero_level() const; bool is_zero_level() const;
void set_sample_volume_range(std::int16_t range); void set_sample_volume_range(std::int16_t range);
@@ -149,7 +149,7 @@ template <bool stereo> class AY38910: public ::Outputs::Speaker::SampleSource<AY
uint8_t data_input_, data_output_; uint8_t data_input_, data_output_;
uint32_t output_volume_; typename Outputs::Speaker::SampleT<stereo>::type output_volume_;
void update_bus(); void update_bus();
PortHandler *port_handler_ = nullptr; PortHandler *port_handler_ = nullptr;

View File

@@ -20,7 +20,7 @@ class Toggle: public Outputs::Speaker::SampleSource<Toggle, false> {
public: public:
Toggle(Concurrency::AsyncTaskQueue<false> &audio_queue); Toggle(Concurrency::AsyncTaskQueue<false> &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 set_sample_volume_range(std::int16_t range);
void skip_samples(const std::size_t number_of_samples); void skip_samples(const std::size_t number_of_samples);

View File

@@ -99,17 +99,14 @@ void Audio::update_channel(int c) {
channels_[c].output |= output; channels_[c].output |= output;
} }
void Audio::get_samples(std::size_t number_of_samples, int16_t *target) { void Audio::get_samples(std::size_t number_of_samples, Outputs::Speaker::StereoSample *target) {
struct Frame { Outputs::Speaker::StereoSample output_level;
int16_t left, right;
} output_level;
Frame *target_frames = reinterpret_cast<Frame *>(target);
size_t c = 0; size_t c = 0;
while(c < number_of_samples) { while(c < number_of_samples) {
// I'm unclear on the details of the time division multiplexing so, // I'm unclear on the details of the time division multiplexing so,
// for now, just sum the outputs. // for now, just sum the outputs.
output_level.left = output_level[0] =
volume_ * volume_ *
(use_direct_output_[0] ? (use_direct_output_[0] ?
channels_[0].amplitude[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 noise_.amplitude[0] * noise_.final_output
)); ));
output_level.right = output_level[1] =
volume_ * volume_ *
(use_direct_output_[1] ? (use_direct_output_[1] ?
channels_[0].amplitude[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) { while(global_divider_ && c < number_of_samples) {
--global_divider_; --global_divider_;
target_frames[c] = output_level; target[c] = output_level;
++c; ++c;
} }
global_divider_ = global_divider_reload_; 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::FourBit)] = poly4_.next();
poly_state_[int(Channel::Distortion::FiveBit)] = poly5_.next(); poly_state_[int(Channel::Distortion::FiveBit)] = poly5_.next();
poly_state_[int(Channel::Distortion::SevenBit)] = poly7_.next(); poly_state_[int(Channel::Distortion::SevenBit)] = poly7_.next();

View File

@@ -37,7 +37,7 @@ class Audio: public Outputs::Speaker::SampleSource<Audio, true> {
// MARK: - SampleSource. // MARK: - SampleSource.
void set_sample_volume_range(int16_t range); 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: private:
Concurrency::AsyncTaskQueue<false> &audio_queue_; Concurrency::AsyncTaskQueue<false> &audio_queue_;

View File

@@ -404,7 +404,12 @@ template <typename SampleSource> class PullLowpass: public LowpassBase<PullLowpa
} }
void get_samples(size_t length, int16_t *target) { void get_samples(size_t length, int16_t *target) {
sample_source_.get_samples(length, target); if constexpr (SampleSource::is_stereo) {
StereoSample *const stereo_target = reinterpret_cast<StereoSample *>(target);
sample_source_.get_samples(length, stereo_target);
} else {
sample_source_.get_samples(length, target);
}
} }
}; };

View File

@@ -12,11 +12,9 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
namespace Outputs::Speaker { #include "../Speaker.hpp"
template <bool stereo> struct SampleT; namespace Outputs::Speaker {
template <> struct SampleT<true> { using type = std::array<std::int16_t, 2>; };
template <> struct SampleT<false> { using type = std::int16_t; };
/*! /*!
A sample source is something that can provide a stream of audio. 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. 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<stereo>::type *target) {
const auto &source = *static_cast<SourceT *>(this); const auto &source = *static_cast<SourceT *>(this);
while(number_of_samples--) { while(number_of_samples--) {
if constexpr (is_stereo) { *target = source.level();
const auto next = source.level(); ++target;
target[0] = next[0];
target[1] = next[1];
target += 2;
} else {
*target = source.level();
++target;
}
} }
} }
@@ -59,7 +50,7 @@ class SampleSource {
if constexpr (&SourceT::advance == &SampleSource<SourceT, stereo>::advance) { if constexpr (&SourceT::advance == &SampleSource<SourceT, stereo>::advance) {
return; return;
} }
std::int16_t scratch_pad[number_of_samples]; typename SampleT<stereo>::type scratch_pad[number_of_samples];
get_samples(number_of_samples, scratch_pad); 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 fill the target with zeroes; @c false if a call might return all zeroes or
might not. might not.
*/ */
bool is_zero_level() const { return false; } bool is_zero_level() const { return false; }
auto level() const { auto level() const { return typename SampleT<stereo>::type(); }
typename SampleT<is_stereo>::type result;
if constexpr (is_stereo) {
result[0] = result[1] = 0;
} else {
result = 0;
}
return result;
}
void advance() {} void advance() {}
/*! /*!

View File

@@ -14,6 +14,14 @@
namespace Outputs::Speaker { namespace Outputs::Speaker {
template <bool stereo> struct SampleT;
template <> struct SampleT<true> { using type = std::array<std::int16_t, 2>; }; // TODO: adopt left/right as per Dave?
template <> struct SampleT<false> { using type = std::int16_t; };
// Shorthands.
using MonoSample = SampleT<false>::type;
using StereoSample = SampleT<true>::type;
/*! /*!
Provides a communication point for sound; machines that have a speaker provide an Provides a communication point for sound; machines that have a speaker provide an
audio output. audio output.