1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-06-18 13:29:41 +00:00

Distinguish sources of samples and of whole buffers.

This commit is contained in:
Thomas Harte 2024-02-09 14:25:40 -05:00
parent c105acf1c7
commit 609d81d75d
17 changed files with 107 additions and 44 deletions

View File

@ -12,12 +12,12 @@
#include "../../Concurrency/AsyncTaskQueue.hpp"
#include "../../Outputs/CRT/CRT.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
namespace MOS::MOS6560 {
// audio state
class AudioGenerator: public Outputs::Speaker::SampleSource<AudioGenerator, false> {
class AudioGenerator: public Outputs::Speaker::BufferSource<AudioGenerator, false> {
public:
AudioGenerator(Concurrency::AsyncTaskQueue<false> &audio_queue);

View File

@ -8,7 +8,7 @@
#pragma once
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
#include "../../Reflection/Struct.hpp"
@ -66,7 +66,7 @@ enum class Personality {
This AY has an attached mono or stereo mixer.
*/
template <bool stereo> class AY38910: public ::Outputs::Speaker::SampleSource<AY38910<stereo>, stereo> {
template <bool stereo> class AY38910: public ::Outputs::Speaker::BufferSource<AY38910<stereo>, stereo> {
public:
/// Creates a new AY38910.
AY38910(Personality, Concurrency::AsyncTaskQueue<false> &);

View File

@ -8,7 +8,7 @@
#pragma once
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
namespace Audio {
@ -16,7 +16,7 @@ namespace Audio {
/*!
Provides a sample source that can programmatically be set to one of two values.
*/
class Toggle: public Outputs::Speaker::SampleSource<Toggle, false> {
class Toggle: public Outputs::Speaker::BufferSource<Toggle, false> {
public:
Toggle(Concurrency::AsyncTaskQueue<false> &audio_queue);

View File

@ -8,7 +8,7 @@
#pragma once
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
namespace Konami {
@ -20,7 +20,7 @@ namespace Konami {
and five channels of output. The original SCC uses the same wave for channels
four and five, the SCC+ supports different waves for the two channels.
*/
class SCC: public ::Outputs::Speaker::SampleSource<SCC, false> {
class SCC: public ::Outputs::Speaker::BufferSource<SCC, false> {
public:
/// Creates a new SCC.
SCC(Concurrency::AsyncTaskQueue<false> &task_queue);

View File

@ -8,12 +8,12 @@
#pragma once
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../../Concurrency/AsyncTaskQueue.hpp"
namespace Yamaha::OPL {
template <typename Child, bool stereo> class OPLBase: public ::Outputs::Speaker::SampleSource<Child, stereo> {
template <typename Child, bool stereo> class OPLBase: public ::Outputs::Speaker::BufferSource<Child, stereo> {
public:
void write(uint16_t address, uint8_t value) {
if(address & 1) {

View File

@ -8,12 +8,12 @@
#pragma once
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
namespace TI {
class SN76489: public Outputs::Speaker::SampleSource<SN76489, false> {
class SN76489: public Outputs::Speaker::BufferSource<SN76489, false> {
public:
enum class Personality {
SN76489,

View File

@ -12,11 +12,11 @@
#include "../../../ClockReceiver/ClockReceiver.hpp"
#include "../../../Concurrency/AsyncTaskQueue.hpp"
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../../Outputs/Speaker/Implementation/BufferSource.hpp"
namespace Apple::IIgs::Sound {
class GLU: public Outputs::Speaker::SampleSource<GLU, false> { // TODO: isn't this stereo?
class GLU: public Outputs::Speaker::BufferSource<GLU, false> { // TODO: isn't this stereo?
public:
GLU(Concurrency::AsyncTaskQueue<false> &audio_queue);

View File

@ -10,7 +10,7 @@
#include "../../../Concurrency/AsyncTaskQueue.hpp"
#include "../../../ClockReceiver/ClockReceiver.hpp"
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include <array>
#include <atomic>
@ -23,7 +23,7 @@ namespace Apple::Macintosh {
Designed to be clocked at half the rate of the real hardware i.e.
a shade less than 4Mhz.
*/
class Audio: public ::Outputs::Speaker::SampleSource<Audio, false> {
class Audio: public ::Outputs::Speaker::BufferSource<Audio, false> {
public:
Audio(Concurrency::AsyncTaskQueue<false> &task_queue);

View File

@ -8,7 +8,7 @@
#pragma once
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../../Concurrency/AsyncTaskQueue.hpp"
namespace Atari2600 {
@ -17,7 +17,7 @@ namespace Atari2600 {
// will give greater resolution to changes in audio state. 1, 2 and 19 are the only divisors of 38.
constexpr int CPUTicksPerAudioTick = 2;
class TIASound: public Outputs::Speaker::SampleSource<TIASound, false> {
class TIASound: public Outputs::Speaker::BufferSource<TIASound, false> {
public:
TIASound(Concurrency::AsyncTaskQueue<false> &audio_queue);

View File

@ -8,12 +8,12 @@
#pragma once
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
namespace Electron {
class SoundGenerator: public ::Outputs::Speaker::SampleSource<SoundGenerator, false> {
class SoundGenerator: public ::Outputs::Speaker::BufferSource<SoundGenerator, false> {
public:
SoundGenerator(Concurrency::AsyncTaskQueue<false> &audio_queue);

View File

@ -13,7 +13,7 @@
#include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../Concurrency/AsyncTaskQueue.hpp"
#include "../../Numeric/LFSR.hpp"
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
namespace Enterprise::Dave {
@ -26,7 +26,7 @@ enum class Interrupt: uint8_t {
/*!
Models the audio-production subset of Dave's behaviour.
*/
class Audio: public Outputs::Speaker::SampleSource<Audio, true> {
class Audio: public Outputs::Speaker::BufferSource<Audio, true> {
public:
Audio(Concurrency::AsyncTaskQueue<false> &audio_queue);

View File

@ -41,7 +41,7 @@
#include "../../Outputs/Log.hpp"
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../Configurable/StandardOptions.hpp"
#include "../../ClockReceiver/ForceInline.hpp"

View File

@ -28,7 +28,7 @@
#include "../../../Outputs/Speaker/Implementation/CompoundSource.hpp"
#include "../../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../../Outputs/Speaker/Implementation/SampleSource.hpp"
#include "../../../Outputs/Speaker/Implementation/BufferSource.hpp"
#include "../../../Storage/Tape/Tape.hpp"
#include "../../../Storage/Tape/Parsers/Spectrum.hpp"

View File

@ -1533,7 +1533,7 @@
4B680CE123A5553100451D43 /* 68000ComparativeTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = 68000ComparativeTests.mm; sourceTree = "<group>"; };
4B680CE323A555CA00451D43 /* 68000 Comparative Tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "68000 Comparative Tests"; sourceTree = "<group>"; };
4B683B002727BE6F0043E541 /* Amiga Blitter Tests */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "Amiga Blitter Tests"; sourceTree = "<group>"; };
4B698D1A1FE768A100696C91 /* SampleSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SampleSource.hpp; sourceTree = "<group>"; };
4B698D1A1FE768A100696C91 /* BufferSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = BufferSource.hpp; sourceTree = "<group>"; };
4B69DEB52AB79E4F0055B217 /* Instruction.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Instruction.cpp; sourceTree = "<group>"; };
4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Tape.cpp; sourceTree = "<group>"; };
4B69FB3C1C4D908A00B5F0AA /* Tape.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Tape.hpp; sourceTree = "<group>"; };
@ -4004,7 +4004,7 @@
isa = PBXGroup;
children = (
4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */,
4B698D1A1FE768A100696C91 /* SampleSource.hpp */,
4B698D1A1FE768A100696C91 /* BufferSource.hpp */,
4B770A961FE9EE770026DC70 /* CompoundSource.hpp */,
);
path = Implementation;

View File

@ -22,7 +22,7 @@ namespace Outputs::Speaker {
by the template parameter to LowpassSpeaker.
*/
template <typename SourceT, bool stereo>
class SampleSource {
class BufferSource {
public:
/*!
Indicates whether this component will write stereo samples.
@ -32,13 +32,7 @@ class SampleSource {
/*!
Should write the next @c number_of_samples to @c target.
*/
void get_samples(std::size_t number_of_samples, typename SampleT<stereo>::type *target) {
const auto &source = *static_cast<SourceT *>(this);
while(number_of_samples--) {
*target = source.level();
++target;
}
}
void get_samples([[maybe_unused]] std::size_t number_of_samples, [[maybe_unused]] typename SampleT<stereo>::type *target) {}
/*!
Should skip the next @c number_of_samples. Subclasses of this SampleSource
@ -46,10 +40,7 @@ class SampleSource {
merely to call get_samples and throw the result away, as per the default
implementation below.
*/
void skip_samples(const std::size_t number_of_samples) {
if constexpr (&SourceT::advance == &SampleSource<SourceT, stereo>::advance) {
return;
}
void skip_samples(std::size_t number_of_samples) {
typename SampleT<stereo>::type scratch_pad[number_of_samples];
get_samples(number_of_samples, scratch_pad);
}
@ -59,9 +50,7 @@ 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 { return typename SampleT<stereo>::type(); }
void advance() {}
bool is_zero_level() const { return false; }
/*!
Sets the proper output range for this sample source; it should write values
@ -82,4 +71,79 @@ class SampleSource {
double average_output_peak() const { return 1.0; }
};
///
template <typename SourceT, bool stereo, int divider = 1>
struct SampleSource: public BufferSource<SourceT, stereo> {
public:
void get_samples(std::size_t number_of_samples, typename SampleT<stereo>::type *target) {
const auto &source = *static_cast<SourceT *>(this);
if constexpr (divider == 1) {
while(number_of_samples--) {
*target = source.level();
++target;
}
} else {
std::size_t c = 0;
// Fill in the tail of any pa3rtially-captured level.
auto level = source.level();
while(c < number_of_samples && master_divider_ != divider) {
target[c] = level;
++c;
++master_divider_;
}
source.advance();
// Provide all full levels.
int whole_steps = (number_of_samples - c) / divider;
while(whole_steps--) {
std::fill(&target[c], &target[c + divider], source.level());
c += divider;
source.advance();
}
// Provide the head of a further partial capture.
level = source.level();
master_divider_ = number_of_samples - c;
std::fill(&target[c], &target[number_of_samples], source.level());
}
}
void skip_samples(std::size_t number_of_samples) {
const auto &source = *static_cast<SourceT *>(this);
if constexpr (&SourceT::advance == &SampleSource::advance) {
return;
}
if constexpr (divider == 1) {
while(--number_of_samples) {
source.advance();
}
} else {
if(number_of_samples >= divider - master_divider_) {
source.advance();
number_of_samples -= (divider - master_divider_);
}
while(number_of_samples > divider) {
advance();
number_of_samples -= divider;
}
master_divider_ = number_of_samples;
}
}
// TODO: use a concept here, when C++20 filters through.
//
// Until then: sample sources should implement this.
auto level() const {
return typename SampleT<stereo>::type();
}
void advance() {}
private:
int master_divider_{};
};
}

View File

@ -8,7 +8,7 @@
#pragma once
#include "SampleSource.hpp"
#include "BufferSource.hpp"
#include <algorithm>
#include <cassert>
@ -31,7 +31,7 @@ template <typename... S> constexpr bool is_stereo() {
An owner may optionally assign relative volumes.
*/
template <typename... T> class CompoundSource:
public Outputs::Speaker::SampleSource<CompoundSource<T...>, ::Outputs::Speaker::is_stereo<T...>()> {
public Outputs::Speaker::BufferSource<CompoundSource<T...>, ::Outputs::Speaker::is_stereo<T...>()> {
private:
template <typename... S> class CompoundSourceHolder {
public:

View File

@ -14,7 +14,6 @@
namespace Outputs::Speaker {
// Shorthands.
using MonoSample = int16_t;
struct StereoSample {
#if TARGET_RT_BIG_ENDIAN