1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-24 12:30:17 +00:00

Introduces the MSX keyboard toggle sample source.

In support of which, it also introduces a means of sample source composition.
This commit is contained in:
Thomas Harte 2017-12-19 21:08:10 -05:00
parent b99ba2bc02
commit 2d892da225
3 changed files with 129 additions and 11 deletions

View File

@ -21,10 +21,46 @@
#include "../ConfigurationTarget.hpp"
#include "../KeyboardMachine.hpp"
#include "../../Outputs/Speaker/Implementation/CompoundSource.hpp"
#include "../../Outputs/Speaker/Implementation/LowpassSpeaker.hpp"
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
namespace MSX {
/*!
Provides a sample source that can programmatically be set to one of two values.
*/
class AudioToggle: public Outputs::Speaker::SampleSource {
public:
AudioToggle(Concurrency::DeferringAsyncTaskQueue &audio_queue) :
audio_queue_(audio_queue) {}
void get_samples(std::size_t number_of_samples, std::int16_t *target) {
for(std::size_t sample = 0; sample < number_of_samples; ++sample) {
target[sample] = level_;
}
}
void skip_samples(const std::size_t number_of_samples) {}
void set_output(bool enabled) {
if(is_enabled_ == enabled) return;
is_enabled_ = enabled;
audio_queue_.defer([=] {
level_ = enabled ? 4096 : 0;
});
}
bool get_output() {
return is_enabled_;
}
private:
bool is_enabled_ = false;
int16_t level_ = 0;
Concurrency::DeferringAsyncTaskQueue &audio_queue_;
};
struct AYPortHandler: public GI::AY38910::PortHandler {
void set_port_output(bool port_b, uint8_t value) {
// printf("AY port %c output: %02x\n", port_b ? 'b' : 'a', value);
@ -47,8 +83,10 @@ class ConcreteMachine:
z80_(*this),
i8255_(i8255_port_handler_),
ay_(audio_queue_),
speaker_(ay_),
i8255_port_handler_(*this) {
audio_toggle_(audio_queue_),
mixer_(ay_, audio_toggle_),
speaker_(mixer_),
i8255_port_handler_(*this, audio_toggle_) {
set_clock_rate(3579545);
std::memset(unpopulated_, 0xff, sizeof(unpopulated_));
clear_all_keys();
@ -96,7 +134,6 @@ class ConcreteMachine:
}
void page_memory(uint8_t value) {
// printf("Page: %02x\n", value);
for(size_t c = 0; c < 4; ++c) {
read_pointers_[c] = memory_slots_[value & 3].read_pointers[c];
write_pointers_[c] = memory_slots_[value & 3].write_pointers[c];
@ -133,7 +170,7 @@ class ConcreteMachine:
break;
case 0xa2:
speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2)));
update_audio();
ay_.set_control_lines(static_cast<GI::AY38910::ControlLines>(GI::AY38910::BC2 | GI::AY38910::BC1));
*cycle.value = ay_.get_data_output();
ay_.set_control_lines(static_cast<GI::AY38910::ControlLines>(0));
@ -161,7 +198,7 @@ class ConcreteMachine:
break;
case 0xa0: case 0xa1:
speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2)));
update_audio();
ay_.set_control_lines(static_cast<GI::AY38910::ControlLines>(GI::AY38910::BDIR | GI::AY38910::BC2 | ((port == 0xa0) ? GI::AY38910::BC1 : 0)));
ay_.set_data_input(*cycle.value);
ay_.set_control_lines(static_cast<GI::AY38910::ControlLines>(0));
@ -195,7 +232,7 @@ class ConcreteMachine:
void flush() {
vdp_->run_for(time_since_vdp_update_.flush());
speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2)));
update_audio();
audio_queue_.perform();
}
@ -256,21 +293,34 @@ class ConcreteMachine:
}
private:
void update_audio() {
speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2)));
}
class i8255PortHandler: public Intel::i8255::PortHandler {
public:
i8255PortHandler(ConcreteMachine &machine) : machine_(machine) {}
i8255PortHandler(ConcreteMachine &machine, AudioToggle &audio_toggle) :
machine_(machine), audio_toggle_(audio_toggle) {}
void set_value(int port, uint8_t value) {
switch(port) {
case 0: machine_.page_memory(value); break;
case 2:
case 2: {
// TODO:
// b7 keyboard click
// b6 caps lock LED
// b5 audio output
// b4 cassette motor relay
// b7: keyboard click
bool new_audio_level = !!(value & 0x80);
if(audio_toggle_.get_output() != new_audio_level) {
machine_.update_audio();
audio_toggle_.set_output(new_audio_level);
}
// b0b3: keyboard line
machine_.set_keyboard_line(value & 0xf);
break;
} break;
default: printf("What what what what?\n"); break;
}
}
@ -284,6 +334,7 @@ class ConcreteMachine:
private:
ConcreteMachine &machine_;
AudioToggle &audio_toggle_;
};
CPU::Z80::Processor<ConcreteMachine, false, false> z80_;
@ -292,7 +343,9 @@ class ConcreteMachine:
Concurrency::DeferringAsyncTaskQueue audio_queue_;
GI::AY38910::AY38910 ay_;
Outputs::Speaker::LowpassSpeaker<GI::AY38910::AY38910> speaker_;
AudioToggle audio_toggle_;
Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle> mixer_;
Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle>> speaker_;
i8255PortHandler i8255_port_handler_;
AYPortHandler ay_port_handler_;

View File

@ -840,6 +840,7 @@
4B71368F1F789C93008B8ED9 /* SegmentParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SegmentParser.cpp; sourceTree = "<group>"; };
4B7136901F789C93008B8ED9 /* SegmentParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = SegmentParser.hpp; sourceTree = "<group>"; };
4B77069C1EC904570053B588 /* Z80.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Z80.hpp; path = Z80/Z80.hpp; sourceTree = "<group>"; };
4B770A961FE9EE770026DC70 /* CompoundSource.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CompoundSource.hpp; sourceTree = "<group>"; };
4B7913CA1DFCD80E00175A82 /* Video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Video.cpp; path = Electron/Video.cpp; sourceTree = "<group>"; };
4B7913CB1DFCD80E00175A82 /* Video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Video.hpp; path = Electron/Video.hpp; sourceTree = "<group>"; };
4B79A4FE1FC9082300EEDAD5 /* TypedDynamicMachine.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = TypedDynamicMachine.hpp; sourceTree = "<group>"; };
@ -2023,6 +2024,7 @@
children = (
4B8EF6071FE5AF830076CCDD /* LowpassSpeaker.hpp */,
4B698D1A1FE768A100696C91 /* SampleSource.hpp */,
4B770A961FE9EE770026DC70 /* CompoundSource.hpp */,
);
path = Implementation;
sourceTree = "<group>";

View File

@ -0,0 +1,63 @@
//
// CompoundSource.hpp
// Clock Signal
//
// Created by Thomas Harte on 19/12/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef CompoundSource_h
#define CompoundSource_h
#include "SampleSource.hpp"
#include <cstring>
namespace Outputs {
namespace Speaker {
/*!
A CompoundSource adds together the sound generated by multiple individual SampleSources.
It's an instance of template metaprogramming; this is the base case.
*/
template <typename... T> class CompoundSource: public Outputs::Speaker::SampleSource {
public:
void get_samples(std::size_t number_of_samples, std::int16_t *target) {
std::memset(target, 0, sizeof(std::int16_t) * number_of_samples);
}
};
/*!
A CompoundSource adds together the sound generated by multiple individual SampleSources.
It's an instance of template metaprogramming; this is the recursive case.
*/
template <typename T, typename... R> class CompoundSource<T, R...>: public Outputs::Speaker::SampleSource {
public:
CompoundSource(T &source, R &...next) : source_(source), next_source_(next...) {}
void get_samples(std::size_t number_of_samples, std::int16_t *target) {
// This is an ugly solution: get whatever the next source supplies and add the local
// results to it, with the degenerate case performing a memset to supply an array of 0.
// TODO: do better. Might need some alteration of SampleSource.
int16_t next_samples[number_of_samples];
next_source_.get_samples(number_of_samples, next_samples);
source_.get_samples(number_of_samples, target);
while(number_of_samples--) {
target[number_of_samples] += next_samples[number_of_samples];
}
}
void skip_samples(const std::size_t number_of_samples) {
source_.skip_samples(number_of_samples);
next_source_.skip_samples(number_of_samples);
}
private:
T &source_;
CompoundSource<R...> next_source_;
};
}
}
#endif /* CompoundSource_h */