diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index a172618ca..5c8269186 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -11,6 +11,7 @@ #include "../../Processors/6502/CPU6502.hpp" #include "../../Outputs/CRT.hpp" +#include "../../Outputs/Speaker.hpp" #include #include "Atari2600Inputs.h" @@ -38,73 +39,20 @@ enum Interrupt: uint8_t { }; enum Key: uint16_t { - KeySpace = 0x0000 | 0x08, - KeyCopy = 0x0000 | 0x02, - KeyRight = 0x0000 | 0x01, - - KeyDelete = 0x0010 | 0x08, - KeyReturn = 0x0010 | 0x04, - KeyDown = 0x0010 | 0x02, - KeyLeft = 0x0010 | 0x01, - - KeyColon = 0x0020 | 0x04, - KeyUp = 0x0020 | 0x02, - KeyMinus = 0x0020 | 0x01, - - KeySlash = 0x0030 | 0x08, - KeySemiColon = 0x0030 | 0x04, - KeyP = 0x0030 | 0x02, - Key0 = 0x0030 | 0x01, - - KeyFullStop = 0x0040 | 0x08, - KeyL = 0x0040 | 0x04, - KeyO = 0x0040 | 0x02, - Key9 = 0x0040 | 0x01, - - KeyComma = 0x0050 | 0x08, - KeyK = 0x0050 | 0x04, - KeyI = 0x0050 | 0x02, - Key8 = 0x0050 | 0x01, - - KeyM = 0x0060 | 0x08, - KeyJ = 0x0060 | 0x04, - KeyU = 0x0060 | 0x02, - Key7 = 0x0060 | 0x01, - - KeyN = 0x0070 | 0x08, - KeyH = 0x0070 | 0x04, - KeyY = 0x0070 | 0x02, - Key6 = 0x0070 | 0x01, - - KeyB = 0x0080 | 0x08, - KeyG = 0x0080 | 0x04, - KeyT = 0x0080 | 0x02, - Key5 = 0x0080 | 0x01, - - KeyV = 0x0090 | 0x08, - KeyF = 0x0090 | 0x04, - KeyR = 0x0090 | 0x02, - Key4 = 0x0090 | 0x01, - - KeyC = 0x00a0 | 0x08, - KeyD = 0x00a0 | 0x04, - KeyE = 0x00a0 | 0x02, - Key3 = 0x00a0 | 0x01, - - KeyX = 0x00b0 | 0x08, - KeyS = 0x00b0 | 0x04, - KeyW = 0x00b0 | 0x02, - Key2 = 0x00b0 | 0x01, - - KeyZ = 0x00c0 | 0x08, - KeyA = 0x00c0 | 0x04, - KeyQ = 0x00c0 | 0x02, - Key1 = 0x00c0 | 0x01, - - KeyShift = 0x00d0 | 0x08, - KeyControl = 0x00d0 | 0x04, - KeyFunc = 0x00d0 | 0x02, - KeyEscape = 0x00d0 | 0x01, + KeySpace = 0x0000 | 0x08, KeyCopy = 0x0000 | 0x02, KeyRight = 0x0000 | 0x01, + KeyDelete = 0x0010 | 0x08, KeyReturn = 0x0010 | 0x04, KeyDown = 0x0010 | 0x02, KeyLeft = 0x0010 | 0x01, + KeyColon = 0x0020 | 0x04, KeyUp = 0x0020 | 0x02, KeyMinus = 0x0020 | 0x01, + KeySlash = 0x0030 | 0x08, KeySemiColon = 0x0030 | 0x04, KeyP = 0x0030 | 0x02, Key0 = 0x0030 | 0x01, + KeyFullStop = 0x0040 | 0x08, KeyL = 0x0040 | 0x04, KeyO = 0x0040 | 0x02, Key9 = 0x0040 | 0x01, + KeyComma = 0x0050 | 0x08, KeyK = 0x0050 | 0x04, KeyI = 0x0050 | 0x02, Key8 = 0x0050 | 0x01, + KeyM = 0x0060 | 0x08, KeyJ = 0x0060 | 0x04, KeyU = 0x0060 | 0x02, Key7 = 0x0060 | 0x01, + KeyN = 0x0070 | 0x08, KeyH = 0x0070 | 0x04, KeyY = 0x0070 | 0x02, Key6 = 0x0070 | 0x01, + KeyB = 0x0080 | 0x08, KeyG = 0x0080 | 0x04, KeyT = 0x0080 | 0x02, Key5 = 0x0080 | 0x01, + KeyV = 0x0090 | 0x08, KeyF = 0x0090 | 0x04, KeyR = 0x0090 | 0x02, Key4 = 0x0090 | 0x01, + KeyC = 0x00a0 | 0x08, KeyD = 0x00a0 | 0x04, KeyE = 0x00a0 | 0x02, Key3 = 0x00a0 | 0x01, + KeyX = 0x00b0 | 0x08, KeyS = 0x00b0 | 0x04, KeyW = 0x00b0 | 0x02, Key2 = 0x00b0 | 0x01, + KeyZ = 0x00c0 | 0x08, KeyA = 0x00c0 | 0x04, KeyQ = 0x00c0 | 0x02, Key1 = 0x00c0 | 0x01, + KeyShift = 0x00d0 | 0x08, KeyControl = 0x00d0 | 0x04, KeyFunc = 0x00d0 | 0x02, KeyEscape = 0x00d0 | 0x01, KeyBreak = 0xffff }; @@ -143,6 +91,9 @@ class Machine: public CPU6502::Processor { inline void update_display(); inline void signal_interrupt(Interrupt interrupt); inline void evaluate_interrupts(); + + class Speaker: public ::Speaker::Filter { + }; }; } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index af606077c..cb526d640 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -330,6 +330,7 @@ 4B1414611B58888700E04248 /* KlausDormannTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KlausDormannTests.swift; sourceTree = ""; }; 4B2409531C45AB05004DA684 /* Speaker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Speaker.cpp; path = ../../Outputs/Speaker.cpp; sourceTree = ""; }; 4B2409541C45AB05004DA684 /* Speaker.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Speaker.hpp; path = ../../Outputs/Speaker.hpp; sourceTree = ""; }; + 4B24095A1C45DF85004DA684 /* Stepper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Stepper.hpp; sourceTree = ""; }; 4B2632551B631A510082A461 /* CRTFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = CRTFrame.h; path = ../../Outputs/CRTFrame.h; sourceTree = ""; }; 4B2E2D941C399D1200138695 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/ElectronDocument.xib; sourceTree = ""; }; 4B2E2D971C3A06EC00138695 /* Atari2600.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Atari2600.cpp; sourceTree = ""; }; @@ -686,6 +687,15 @@ name = "Test Binaries"; sourceTree = ""; }; + 4B2409591C45DF85004DA684 /* SignalProcessing */ = { + isa = PBXGroup; + children = ( + 4B24095A1C45DF85004DA684 /* Stepper.hpp */, + ); + name = SignalProcessing; + path = ../../SignalProcessing; + sourceTree = ""; + }; 4B2E2D961C3A06EC00138695 /* Atari2600 */ = { isa = PBXGroup; children = ( @@ -1028,12 +1038,13 @@ 4BB73E951B587A5100552FC2 = { isa = PBXGroup; children = ( - 4B366DFD1B5C165F0026627B /* Outputs */, - 4BB73EDC1B587CA500552FC2 /* Machines */, - 4BB73EDD1B587CA500552FC2 /* Processors */, 4BB73EA01B587A5100552FC2 /* Clock Signal */, 4BB73EB51B587A5100552FC2 /* Clock SignalTests */, 4BB73EC01B587A5100552FC2 /* Clock SignalUITests */, + 4BB73EDC1B587CA500552FC2 /* Machines */, + 4B366DFD1B5C165F0026627B /* Outputs */, + 4BB73EDD1B587CA500552FC2 /* Processors */, + 4B2409591C45DF85004DA684 /* SignalProcessing */, 4BB73E9F1B587A5100552FC2 /* Products */, ); sourceTree = ""; diff --git a/Outputs/Speaker.cpp b/Outputs/Speaker.cpp index fa8c60291..e393dbc76 100644 --- a/Outputs/Speaker.cpp +++ b/Outputs/Speaker.cpp @@ -7,3 +7,6 @@ // #include "Speaker.hpp" + +using namespace Speaker; + diff --git a/Outputs/Speaker.hpp b/Outputs/Speaker.hpp index 21312d5b2..59eec9862 100644 --- a/Outputs/Speaker.hpp +++ b/Outputs/Speaker.hpp @@ -10,6 +10,7 @@ #define Speaker_hpp #include +#include "../SignalProcessing/Stepper.hpp" namespace Speaker { @@ -20,11 +21,70 @@ class Delegate { template class Filter { public: - void set_output_rate(int cycles_per_second); - void set_delegate(Delegate *delegate); + void set_output_rate(int cycles_per_second, int buffer_size) + { + _output_cycles_per_second = cycles_per_second; + if(_buffer_size != buffer_size) + { + delete[] _buffer_in_progress; + _buffer_in_progress = new uint16_t[buffer_size]; + _buffer_size = buffer_size; + } + set_needs_updated_filter_coefficients(); + } - void set_input_rate(int cycles_per_second); - void run_for_cycles(int input_cycles); + void set_output_quality(int number_of_taps) + { + _number_of_taps = number_of_taps; + set_needs_updated_filter_coefficients(); + } + + void set_delegate(Delegate *delegate) + { + _delegate = delegate; + } + + void set_input_rate(int cycles_per_second) + { + _input_cycles_per_second = cycles_per_second; + set_needs_updated_filter_coefficients(); + } + + void run_for_cycles(int input_cycles) + { + if(_coefficients_are_dirty) update_filter_coefficients(); + + // point sample for now, as a temporary measure + while(input_cycles--) + { + static_cast(this)->perform_bus_operation(); + } + } + + private: + uint16_t *_buffer_in_progress; + int _buffer_size; + int _buffer_in_progress_pointer; + int _number_of_taps; + bool _coefficients_are_dirty; + Delegate *_delegate; + SignalProcessing::Stepper *_stepper; + + int _input_cycles_per_second, _output_cycles_per_second; + + void set_needs_updated_filter_coefficients() + { + _coefficients_are_dirty = true; + } + + void update_filter_coefficients() + { + _coefficients_are_dirty = false; + _buffer_in_progress_pointer = 0; + + delete[] _stepper; + _stepper = Stepper(_input_cycles_per_second, _output_cycles_per_second); + } }; } diff --git a/SignalProcessing/Stepper.hpp b/SignalProcessing/Stepper.hpp new file mode 100644 index 000000000..ad61480c4 --- /dev/null +++ b/SignalProcessing/Stepper.hpp @@ -0,0 +1,46 @@ +// +// Stepper.hpp +// Clock Signal +// +// Created by Thomas Harte on 12/01/2016. +// Copyright © 2016 Thomas Harte. All rights reserved. +// + +#ifndef Stepper_hpp +#define Stepper_hpp + +#include + +namespace SignalProcessing { + +class Stepper +{ + public: + Stepper(uint64_t input_rate, uint64_t output_rate) + { + whole_step_ = output_rate / input_rate; + adjustment_up_ = (int64_t)(output_rate % input_rate) << 1; + adjustment_down_ = (int64_t)input_rate << 1; + } + + inline uint64_t update() + { + uint64_t update = whole_step_; + accumulated_error_ += adjustment_up_; + if(accumulated_error_ > 0) + { + update++; + accumulated_error_ -= adjustment_down_; + } + return update; + } + + private: + uint64_t whole_step_; + int64_t adjustment_up_, adjustment_down_; + int64_t accumulated_error_; +}; + +} + +#endif /* Stepper_hpp */