mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
Merge pull request #330 from TomHarte/SCC
Adds emulation of the Konami SCC
This commit is contained in:
commit
5d0832613f
@ -66,7 +66,7 @@ AY38910::AY38910(Concurrency::DeferringAsyncTaskQueue &task_queue) : task_queue_
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
|
void AY38910::get_samples(std::size_t number_of_samples, int16_t *target) {
|
||||||
unsigned int c = 0;
|
std::size_t c = 0;
|
||||||
while((master_divider_&7) && c < number_of_samples) {
|
while((master_divider_&7) && c < number_of_samples) {
|
||||||
target[c] = output_volume_;
|
target[c] = output_volume_;
|
||||||
master_divider_++;
|
master_divider_++;
|
||||||
|
107
Components/KonamiSCC/KonamiSCC.cpp
Normal file
107
Components/KonamiSCC/KonamiSCC.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
//
|
||||||
|
// KonamiSCC.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 06/01/2018.
|
||||||
|
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "KonamiSCC.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace Konami;
|
||||||
|
|
||||||
|
SCC::SCC(Concurrency::DeferringAsyncTaskQueue &task_queue) :
|
||||||
|
task_queue_(task_queue) {}
|
||||||
|
|
||||||
|
bool SCC::is_silent() {
|
||||||
|
return !(channel_enable_ & 0x1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCC::get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
||||||
|
if(is_silent()) {
|
||||||
|
std::memset(target, 0, sizeof(std::int16_t) * number_of_samples);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t c = 0;
|
||||||
|
while((master_divider_&7) && c < number_of_samples) {
|
||||||
|
target[c] = output_volume_;
|
||||||
|
master_divider_++;
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(c < number_of_samples) {
|
||||||
|
for(int channel = 0; channel < 5; ++channel) {
|
||||||
|
if(channels_[channel].tone_counter) channels_[channel].tone_counter--;
|
||||||
|
else {
|
||||||
|
channels_[channel].offset = (channels_[channel].offset + 1) & 0x1f;
|
||||||
|
channels_[channel].tone_counter = channels_[channel].period;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate_output_volume();
|
||||||
|
|
||||||
|
for(int ic = 0; ic < 8 && c < number_of_samples; ++ic) {
|
||||||
|
target[c] = output_volume_;
|
||||||
|
c++;
|
||||||
|
master_divider_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCC::write(uint16_t address, uint8_t value) {
|
||||||
|
address &= 0xff;
|
||||||
|
if(address < 0x80) ram_[address] = value;
|
||||||
|
|
||||||
|
task_queue_.defer([=] {
|
||||||
|
// Check for a write into waveform memory.
|
||||||
|
if(address < 0x80) {
|
||||||
|
waves_[address >> 5].samples[address & 0x1f] = value;
|
||||||
|
} else switch(address) {
|
||||||
|
default: break;
|
||||||
|
|
||||||
|
case 0x80: case 0x82: case 0x84: case 0x86: case 0x88: {
|
||||||
|
int channel = (address - 0x80) >> 1;
|
||||||
|
channels_[channel].period = (channels_[channel].period & ~0xff) | value;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 0x81: case 0x83: case 0x85: case 0x87: case 0x89: {
|
||||||
|
int channel = (address - 0x80) >> 1;
|
||||||
|
channels_[channel].period = (channels_[channel].period & 0xff) | ((value & 0xf) << 8);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e:
|
||||||
|
channels_[address - 0x8a].amplitude = value & 0xf;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x8f:
|
||||||
|
channel_enable_ = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate_output_volume();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCC::evaluate_output_volume() {
|
||||||
|
output_volume_ =
|
||||||
|
static_cast<int16_t>(
|
||||||
|
(
|
||||||
|
(channel_enable_ & 0x01) ? static_cast<int8_t>(waves_[0].samples[channels_[0].offset]) * channels_[0].amplitude : 0 +
|
||||||
|
(channel_enable_ & 0x02) ? static_cast<int8_t>(waves_[1].samples[channels_[1].offset]) * channels_[1].amplitude : 0 +
|
||||||
|
(channel_enable_ & 0x04) ? static_cast<int8_t>(waves_[2].samples[channels_[2].offset]) * channels_[2].amplitude : 0 +
|
||||||
|
(channel_enable_ & 0x08) ? static_cast<int8_t>(waves_[3].samples[channels_[3].offset]) * channels_[3].amplitude : 0 +
|
||||||
|
(channel_enable_ & 0x10) ? static_cast<int8_t>(waves_[3].samples[channels_[4].offset]) * channels_[4].amplitude : 0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t SCC::read(uint16_t address) {
|
||||||
|
address &= 0xff;
|
||||||
|
if(address < 0x80) {
|
||||||
|
return ram_[address];
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
}
|
72
Components/KonamiSCC/KonamiSCC.hpp
Normal file
72
Components/KonamiSCC/KonamiSCC.hpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//
|
||||||
|
// KonamiSCC.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 06/01/2018.
|
||||||
|
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef KonamiSCC_hpp
|
||||||
|
#define KonamiSCC_hpp
|
||||||
|
|
||||||
|
#include "../../Outputs/Speaker/Implementation/SampleSource.hpp"
|
||||||
|
#include "../../Concurrency/AsyncTaskQueue.hpp"
|
||||||
|
|
||||||
|
namespace Konami {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Provides an emulation of Konami's Sound Creative Chip ('SCC').
|
||||||
|
|
||||||
|
The SCC is a primitive wavetable synthesis chip, offering 32-sample tables,
|
||||||
|
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 {
|
||||||
|
public:
|
||||||
|
/// Creates a new SCC.
|
||||||
|
SCC(Concurrency::DeferringAsyncTaskQueue &task_queue);
|
||||||
|
|
||||||
|
/// As per ::SampleSource; provides a broadphase test for silence.
|
||||||
|
bool is_silent();
|
||||||
|
|
||||||
|
/// As per ::SampleSource; provides audio output.
|
||||||
|
void get_samples(std::size_t number_of_samples, std::int16_t *target);
|
||||||
|
|
||||||
|
/// Writes to the SCC.
|
||||||
|
void write(uint16_t address, uint8_t value);
|
||||||
|
|
||||||
|
/// Reads from the SCC.
|
||||||
|
uint8_t read(uint16_t address);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Concurrency::DeferringAsyncTaskQueue &task_queue_;
|
||||||
|
|
||||||
|
// State from here on down is accessed ony from the audio thread.
|
||||||
|
int master_divider_ = 0;
|
||||||
|
int16_t output_volume_ = 0;
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
int period = 0;
|
||||||
|
int amplitude = 0;
|
||||||
|
|
||||||
|
int tone_counter = 0;
|
||||||
|
int offset = 0;
|
||||||
|
} channels_[5];
|
||||||
|
|
||||||
|
struct Wavetable {
|
||||||
|
std::uint8_t samples[32];
|
||||||
|
} waves_[4];
|
||||||
|
|
||||||
|
std::uint8_t channel_enable_ = 0;
|
||||||
|
std::uint8_t test_register_ = 0;
|
||||||
|
|
||||||
|
void evaluate_output_volume();
|
||||||
|
|
||||||
|
// This keeps a copy of wave memory that is accessed from the
|
||||||
|
// main emulation thread.
|
||||||
|
std::uint8_t ram_[128];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* KonamiSCC_hpp */
|
@ -10,16 +10,17 @@
|
|||||||
#define KonamiWithSCC_hpp
|
#define KonamiWithSCC_hpp
|
||||||
|
|
||||||
#include "../ROMSlotHandler.hpp"
|
#include "../ROMSlotHandler.hpp"
|
||||||
|
#include "../../../Components/KonamiSCC/KonamiSCC.hpp"
|
||||||
|
|
||||||
namespace MSX {
|
namespace MSX {
|
||||||
namespace Cartridge {
|
namespace Cartridge {
|
||||||
|
|
||||||
class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
||||||
public:
|
public:
|
||||||
KonamiWithSCCROMSlotHandler(MSX::MemoryMap &map, int slot) :
|
KonamiWithSCCROMSlotHandler(MSX::MemoryMap &map, int slot, Konami::SCC &scc) :
|
||||||
map_(map), slot_(slot) {}
|
map_(map), slot_(slot), scc_(scc) {}
|
||||||
|
|
||||||
void write(uint16_t address, uint8_t value) {
|
void write(uint16_t address, uint8_t value) override {
|
||||||
switch(address >> 11) {
|
switch(address >> 11) {
|
||||||
default: break;
|
default: break;
|
||||||
case 0x0a:
|
case 0x0a:
|
||||||
@ -29,7 +30,16 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
|||||||
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
map_.map(slot_, value * 8192, 0x6000, 0x2000);
|
||||||
break;
|
break;
|
||||||
case 0x12:
|
case 0x12:
|
||||||
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
if((value&0x3f) == 0x3f) {
|
||||||
|
scc_is_visible_ = true;
|
||||||
|
map_.unmap(slot_, 0x8000, 0x2000);
|
||||||
|
} else {
|
||||||
|
scc_is_visible_ = false;
|
||||||
|
map_.map(slot_, value * 8192, 0x8000, 0x2000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x13:
|
||||||
|
if(scc_is_visible_) scc_.write(address, value);
|
||||||
break;
|
break;
|
||||||
case 0x16:
|
case 0x16:
|
||||||
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
map_.map(slot_, value * 8192, 0xa000, 0x2000);
|
||||||
@ -37,9 +47,18 @@ class KonamiWithSCCROMSlotHandler: public ROMSlotHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t read(uint16_t address) override {
|
||||||
|
if(scc_is_visible_ && address >= 0x9800 && address < 0xa000) {
|
||||||
|
return scc_.read(address);
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MSX::MemoryMap &map_;
|
MSX::MemoryMap &map_;
|
||||||
int slot_;
|
int slot_;
|
||||||
|
Konami::SCC &scc_;
|
||||||
|
bool scc_is_visible_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "../../Components/9918/9918.hpp"
|
#include "../../Components/9918/9918.hpp"
|
||||||
#include "../../Components/8255/i8255.hpp"
|
#include "../../Components/8255/i8255.hpp"
|
||||||
#include "../../Components/AY38910/AY38910.hpp"
|
#include "../../Components/AY38910/AY38910.hpp"
|
||||||
|
#include "../../Components/KonamiSCC/KonamiSCC.hpp"
|
||||||
|
|
||||||
#include "../../Storage/Tape/Parsers/MSX.hpp"
|
#include "../../Storage/Tape/Parsers/MSX.hpp"
|
||||||
#include "../../Storage/Tape/Tape.hpp"
|
#include "../../Storage/Tape/Tape.hpp"
|
||||||
@ -120,7 +121,8 @@ class ConcreteMachine:
|
|||||||
i8255_(i8255_port_handler_),
|
i8255_(i8255_port_handler_),
|
||||||
ay_(audio_queue_),
|
ay_(audio_queue_),
|
||||||
audio_toggle_(audio_queue_),
|
audio_toggle_(audio_queue_),
|
||||||
mixer_(ay_, audio_toggle_),
|
scc_(audio_queue_),
|
||||||
|
mixer_(ay_, audio_toggle_, scc_),
|
||||||
speaker_(mixer_),
|
speaker_(mixer_),
|
||||||
tape_player_(3579545 * 2),
|
tape_player_(3579545 * 2),
|
||||||
i8255_port_handler_(*this, audio_toggle_, tape_player_),
|
i8255_port_handler_(*this, audio_toggle_, tape_player_),
|
||||||
@ -167,7 +169,7 @@ class ConcreteMachine:
|
|||||||
break;
|
break;
|
||||||
case StaticAnalyser::MSXCartridgeType::KonamiWithSCC:
|
case StaticAnalyser::MSXCartridgeType::KonamiWithSCC:
|
||||||
// TODO: enable an SCC.
|
// TODO: enable an SCC.
|
||||||
memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1));
|
memory_slots_[1].set_handler(new Cartridge::KonamiWithSCCROMSlotHandler(*this, 1, scc_));
|
||||||
break;
|
break;
|
||||||
case StaticAnalyser::MSXCartridgeType::ASCII8kb:
|
case StaticAnalyser::MSXCartridgeType::ASCII8kb:
|
||||||
memory_slots_[1].set_handler(new Cartridge::ASCII8kbROMSlotHandler(*this, 1));
|
memory_slots_[1].set_handler(new Cartridge::ASCII8kbROMSlotHandler(*this, 1));
|
||||||
@ -317,6 +319,7 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
int slot_hit = (paged_memory_ >> ((address >> 14) * 2)) & 3;
|
||||||
if(memory_slots_[slot_hit].handler) {
|
if(memory_slots_[slot_hit].handler) {
|
||||||
|
update_audio();
|
||||||
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
memory_slots_[slot_hit].handler->run_for(memory_slots_[slot_hit].cycles_since_update.flush());
|
||||||
memory_slots_[slot_hit].handler->write(address, *cycle.value);
|
memory_slots_[slot_hit].handler->write(address, *cycle.value);
|
||||||
}
|
}
|
||||||
@ -568,8 +571,9 @@ class ConcreteMachine:
|
|||||||
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
Concurrency::DeferringAsyncTaskQueue audio_queue_;
|
||||||
GI::AY38910::AY38910 ay_;
|
GI::AY38910::AY38910 ay_;
|
||||||
AudioToggle audio_toggle_;
|
AudioToggle audio_toggle_;
|
||||||
Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle> mixer_;
|
Konami::SCC scc_;
|
||||||
Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle>> speaker_;
|
Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle, Konami::SCC> mixer_;
|
||||||
|
Outputs::Speaker::LowpassSpeaker<Outputs::Speaker::CompoundSource<GI::AY38910::AY38910, AudioToggle, Konami::SCC>> speaker_;
|
||||||
|
|
||||||
Storage::Tape::BinaryTapePlayer tape_player_;
|
Storage::Tape::BinaryTapePlayer tape_player_;
|
||||||
bool use_fast_tape_ = false;
|
bool use_fast_tape_ = false;
|
||||||
|
@ -205,6 +205,8 @@
|
|||||||
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; };
|
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518971F75FD1B00926311 /* OricMFMDSK.cpp */; };
|
||||||
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; };
|
4B4518A51F75FD1C00926311 /* SSD.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4518991F75FD1B00926311 /* SSD.cpp */; };
|
||||||
4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; };
|
4B4A76301DB1A3FA007AAE2E /* AY38910.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */; };
|
||||||
|
4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
||||||
|
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */; };
|
||||||
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
4B4DC8211D2C2425003C5BF8 /* Vic20.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */; };
|
||||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
||||||
4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */; };
|
4B5073071DDD3B9400C48FBD /* ArrayBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */; };
|
||||||
@ -797,6 +799,8 @@
|
|||||||
4B4518A81F76022000926311 /* DiskImageImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImageImplementation.hpp; sourceTree = "<group>"; };
|
4B4518A81F76022000926311 /* DiskImageImplementation.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiskImageImplementation.hpp; sourceTree = "<group>"; };
|
||||||
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AY38910.cpp; path = AY38910/AY38910.cpp; sourceTree = "<group>"; };
|
4B4A762E1DB1A3FA007AAE2E /* AY38910.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AY38910.cpp; path = AY38910/AY38910.cpp; sourceTree = "<group>"; };
|
||||||
4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AY38910.hpp; path = AY38910/AY38910.hpp; sourceTree = "<group>"; };
|
4B4A762F1DB1A3FA007AAE2E /* AY38910.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = AY38910.hpp; path = AY38910/AY38910.hpp; sourceTree = "<group>"; };
|
||||||
|
4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = KonamiSCC.cpp; sourceTree = "<group>"; };
|
||||||
|
4B4B1A3B200198C900A0F866 /* KonamiSCC.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = KonamiSCC.hpp; sourceTree = "<group>"; };
|
||||||
4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Vic20.cpp; sourceTree = "<group>"; };
|
4B4DC81F1D2C2425003C5BF8 /* Vic20.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Vic20.cpp; sourceTree = "<group>"; };
|
||||||
4B4DC8201D2C2425003C5BF8 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Vic20.hpp; sourceTree = "<group>"; };
|
4B4DC8201D2C2425003C5BF8 /* Vic20.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Vic20.hpp; sourceTree = "<group>"; };
|
||||||
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
|
4B4DC8271D2C2470003C5BF8 /* C1540.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = C1540.hpp; sourceTree = "<group>"; };
|
||||||
@ -1801,6 +1805,15 @@
|
|||||||
name = AY38910;
|
name = AY38910;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
4B4B1A39200198C900A0F866 /* KonamiSCC */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
4B4B1A3A200198C900A0F866 /* KonamiSCC.cpp */,
|
||||||
|
4B4B1A3B200198C900A0F866 /* KonamiSCC.hpp */,
|
||||||
|
);
|
||||||
|
path = KonamiSCC;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
4B4DC81D1D2C2425003C5BF8 /* Commodore */ = {
|
4B4DC81D1D2C2425003C5BF8 /* Commodore */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -2607,11 +2620,12 @@
|
|||||||
4BC9DF4B1D04691600F44158 /* 6522 */,
|
4BC9DF4B1D04691600F44158 /* 6522 */,
|
||||||
4B1E85791D174DEC001EF87D /* 6532 */,
|
4B1E85791D174DEC001EF87D /* 6532 */,
|
||||||
4BC9DF4C1D04691600F44158 /* 6560 */,
|
4BC9DF4C1D04691600F44158 /* 6560 */,
|
||||||
4B4A762D1DB1A35C007AAE2E /* AY38910 */,
|
|
||||||
4BE845221F2FF7F400A5EA22 /* 6845 */,
|
4BE845221F2FF7F400A5EA22 /* 6845 */,
|
||||||
4BD9137C1F3115AC009BCF85 /* 8255 */,
|
4BD9137C1F3115AC009BCF85 /* 8255 */,
|
||||||
4BBC951F1F368D87008F4C34 /* 8272 */,
|
4BBC951F1F368D87008F4C34 /* 8272 */,
|
||||||
4B0E04F71FC9F2C800F43484 /* 9918 */,
|
4B0E04F71FC9F2C800F43484 /* 9918 */,
|
||||||
|
4B4A762D1DB1A35C007AAE2E /* AY38910 */,
|
||||||
|
4B4B1A39200198C900A0F866 /* KonamiSCC */,
|
||||||
);
|
);
|
||||||
name = Components;
|
name = Components;
|
||||||
path = ../../Components;
|
path = ../../Components;
|
||||||
@ -3319,6 +3333,7 @@
|
|||||||
4B055AA31FAE85DF0060FFFF /* ImplicitSectors.cpp in Sources */,
|
4B055AA31FAE85DF0060FFFF /* ImplicitSectors.cpp in Sources */,
|
||||||
4B055AAE1FAE85FD0060FFFF /* TrackSerialiser.cpp in Sources */,
|
4B055AAE1FAE85FD0060FFFF /* TrackSerialiser.cpp in Sources */,
|
||||||
4B055A981FAE85C50060FFFF /* Drive.cpp in Sources */,
|
4B055A981FAE85C50060FFFF /* Drive.cpp in Sources */,
|
||||||
|
4B4B1A3D200198CA00A0F866 /* KonamiSCC.cpp in Sources */,
|
||||||
4B055AE21FAE9B6F0060FFFF /* CRTOpenGL.cpp in Sources */,
|
4B055AE21FAE9B6F0060FFFF /* CRTOpenGL.cpp in Sources */,
|
||||||
4B055AC31FAE9AE80060FFFF /* AmstradCPC.cpp in Sources */,
|
4B055AC31FAE9AE80060FFFF /* AmstradCPC.cpp in Sources */,
|
||||||
4B055A9E1FAE85DA0060FFFF /* G64.cpp in Sources */,
|
4B055A9E1FAE85DA0060FFFF /* G64.cpp in Sources */,
|
||||||
@ -3445,6 +3460,7 @@
|
|||||||
4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */,
|
4B8FE2221DA19FB20090D3CE /* MachinePanel.swift in Sources */,
|
||||||
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */,
|
4B4518A41F75FD1C00926311 /* OricMFMDSK.cpp in Sources */,
|
||||||
4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */,
|
4BBB14311CD2CECE00BDB55C /* IntermediateShader.cpp in Sources */,
|
||||||
|
4B4B1A3C200198CA00A0F866 /* KonamiSCC.cpp in Sources */,
|
||||||
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */,
|
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.mm in Sources */,
|
||||||
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */,
|
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */,
|
||||||
4B4518811F75E91A00926311 /* PCMPatchedTrack.cpp in Sources */,
|
4B4518811F75E91A00926311 /* PCMPatchedTrack.cpp in Sources */,
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
enableASanStackUseAfterReturn = "YES"
|
||||||
disableMainThreadChecker = "YES"
|
disableMainThreadChecker = "YES"
|
||||||
language = ""
|
language = ""
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
|
@ -17,6 +17,7 @@ SOURCES += glob.glob('../../Components/8272/*.cpp')
|
|||||||
SOURCES += glob.glob('../../Components/9918/*.cpp')
|
SOURCES += glob.glob('../../Components/9918/*.cpp')
|
||||||
SOURCES += glob.glob('../../Components/9918/Implementation/*.cpp')
|
SOURCES += glob.glob('../../Components/9918/Implementation/*.cpp')
|
||||||
SOURCES += glob.glob('../../Components/AY38910/*.cpp')
|
SOURCES += glob.glob('../../Components/AY38910/*.cpp')
|
||||||
|
SOURCES += glob.glob('../../Components/KonamiSCC/*.cpp')
|
||||||
|
|
||||||
SOURCES += glob.glob('../../Concurrency/*.cpp')
|
SOURCES += glob.glob('../../Concurrency/*.cpp')
|
||||||
|
|
||||||
|
@ -31,19 +31,22 @@ template <typename... T> class CompoundSource: public Outputs::Speaker::SampleSo
|
|||||||
A CompoundSource adds together the sound generated by multiple individual SampleSources.
|
A CompoundSource adds together the sound generated by multiple individual SampleSources.
|
||||||
It's an instance of template metaprogramming; this is the recursive case.
|
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 {
|
template <typename T, typename... R> class CompoundSource<T, R...>:
|
||||||
|
public Outputs::Speaker::SampleSource {
|
||||||
public:
|
public:
|
||||||
CompoundSource(T &source, R &...next) : source_(source), next_source_(next...) {}
|
CompoundSource(T &source, R &...next) : source_(source), next_source_(next...) {}
|
||||||
|
|
||||||
void get_samples(std::size_t number_of_samples, std::int16_t *target) {
|
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
|
if(source_.is_silent()) {
|
||||||
// results to it, with the degenerate case performing a memset to supply an array of 0.
|
source_.skip_samples(number_of_samples);
|
||||||
// TODO: do better. Might need some alteration of SampleSource.
|
next_source_.get_samples(number_of_samples, target);
|
||||||
int16_t next_samples[number_of_samples];
|
} else {
|
||||||
next_source_.get_samples(number_of_samples, next_samples);
|
int16_t next_samples[number_of_samples];
|
||||||
source_.get_samples(number_of_samples, target);
|
next_source_.get_samples(number_of_samples, next_samples);
|
||||||
while(number_of_samples--) {
|
source_.get_samples(number_of_samples, target);
|
||||||
target[number_of_samples] += next_samples[number_of_samples];
|
while(number_of_samples--) {
|
||||||
|
target[number_of_samples] += next_samples[number_of_samples];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,6 +56,7 @@ template <typename T, typename... R> class CompoundSource<T, R...>: public Outpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool is_sleeping_ = false;
|
||||||
T &source_;
|
T &source_;
|
||||||
CompoundSource<R...> next_source_;
|
CompoundSource<R...> next_source_;
|
||||||
};
|
};
|
||||||
|
@ -37,6 +37,15 @@ class SampleSource {
|
|||||||
std::int16_t scratch_pad[number_of_samples];
|
std::int16_t scratch_pad[number_of_samples];
|
||||||
get_samples(number_of_samples, scratch_pad);
|
get_samples(number_of_samples, scratch_pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns @c true if it is trivially true that a call to get_samples would just
|
||||||
|
fill the target with zeroes; @c false if a call might return all zeroes or
|
||||||
|
might not.
|
||||||
|
*/
|
||||||
|
bool is_silent() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user