mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-11 08:30:55 +00:00
Distinguish sources of samples and of whole buffers.
This commit is contained in:
parent
c105acf1c7
commit
609d81d75d
@ -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);
|
||||
|
||||
|
@ -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> &);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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_{};
|
||||
};
|
||||
|
||||
}
|
@ -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:
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
namespace Outputs::Speaker {
|
||||
|
||||
// Shorthands.
|
||||
using MonoSample = int16_t;
|
||||
struct StereoSample {
|
||||
#if TARGET_RT_BIG_ENDIAN
|
||||
|
Loading…
x
Reference in New Issue
Block a user