Skip to content

Commit

Permalink
Refactor ISR to avoid a divide, fix missing retune on CC reset, fix o…
Browse files Browse the repository at this point in the history
…scillator reset on masterclock rollover.
  • Loading branch information
anarkiwi committed Aug 8, 2022
1 parent d72d6e9 commit 23f18ac
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 71 deletions.
69 changes: 32 additions & 37 deletions CRIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ DigitalPin<coilOutPin> _coilOutPin(OUTPUT, LOW);
DigitalPin<diagOutPin> _diagOutPin(OUTPUT, LOW);
DigitalPin<speakerOutPin> _speakerOutPin(OUTPUT, LOW);

CRIO::CRIO() : scheduled(false), slipTick(false), _oneShotPulseUs(0), _multiShotPulses(0), _pulseState(0), pw(pulseWindowUs), maxPitch(maxMidiPitch), breakoutUs(minBreakoutUs), _ticksSinceLastPulse(0) {
CRIO::CRIO() : scheduled(false), _remainingPulseUs(0), _pulseState(0), pw(pulseWindowUs), maxPitch(maxMidiPitch), breakoutUs(minBreakoutUs), _ticksSinceLastPulse(0), handlePulsePtr(&CRIO::handleNoPulse) {
}

bool CRIO::percussionEnabled() {
Expand All @@ -41,59 +41,54 @@ inline void CRIO::pulseOff() {
}

bool CRIO::handlePulse() {
return (this->*handlePulsePtr)();
}

bool CRIO::handleNoPulse() {
++_ticksSinceLastPulse;
if (scheduled) {
return false;
}
if (_multiShotPulses == 0) {
if (_oneShotPulseUs) {
pulseOn();
delayMicroseconds(_oneShotPulseUs);
pulseOff();
_oneShotPulseUs = 0;
return true;
}
pulseOff();
return false;
}
return false;
}

bool CRIO::handleLongPulse() {
pulseOn();
--_multiShotPulses;
++_ticksSinceLastPulse;
_remainingPulseUs -= masterClockPeriodUs;
if (_remainingPulseUs <= masterClockPeriodUs) {
handlePulsePtr = &CRIO::handleShortPulse;
}
return false;
}

void CRIO::startPulse() {
if (scheduled) {
_ticksSinceLastPulse = 0;
scheduled = false;
bool CRIO::handleShortPulse() {
if (_remainingPulseUs) {
pulseOn();
delayMicroseconds(_remainingPulseUs);
pulseOff();
_remainingPulseUs = 0;
++_ticksSinceLastPulse;
handlePulsePtr = &CRIO::handleNoPulse;
return true;
}
pulseOff();
++_ticksSinceLastPulse;
handlePulsePtr = &CRIO::handleNoPulse;
return false;
}

void CRIO::schedulePulse(cr_fp_t pulseUs) {
if (scheduled) {
return;
}
if (pulseUs == 0) {
return;
}
if (_ticksSinceLastPulse < pulseGuardTicks) {
return;
}
scheduled = true;
_oneShotPulseUs = cr_pulse_t(roundFixed(pulseUs));
if (_oneShotPulseUs >= cr_pulse_t(masterClockPeriodUs)) {
_multiShotPulses = _oneShotPulseUs / masterClockPeriodUs;
_oneShotPulseUs -= _multiShotPulses * masterClockPeriodUs;
_remainingPulseUs = cr_pulse_t(roundFixed(pulseUs));
if (_remainingPulseUs > masterClockPeriodUs) {
handlePulsePtr = &CRIO::handleLongPulse;
} else {
_multiShotPulses = 0;
}
if (_oneShotPulseUs) {
if (_oneShotPulseUs > oneShotRemainderPulsePadUs) {
++_multiShotPulses;
_oneShotPulseUs = 0;
} else if (_oneShotPulseUs < oneShotPulsePadUs) { // cppcheck-suppress knownConditionTrueFalse
_oneShotPulseUs = oneShotPulsePadUs;
}
handlePulsePtr = &CRIO::handleShortPulse;
}
_ticksSinceLastPulse = 0;
}

bool CRIO::pollPots() {
Expand Down
11 changes: 5 additions & 6 deletions CRIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ const uint8_t lcdWidth = 16;
const uint8_t lcdLines = 2;
const uint8_t analogBits = 12;
const cr_fp_t maxAnalogRead = cr_fp_t(1) / cr_fp_t(4095);
const uint8_t oneShotPulsePadUs = 1;
const uint8_t oneShotRemainderPulsePadUs = masterClockPeriodUs - oneShotPulsePadUs;
const uint8_t potPins = 3;
const uint8_t potSampleWindow = 16;

Expand All @@ -34,7 +32,6 @@ typedef struct {
class CRIO {
public:
CRIO();
void startPulse();
void schedulePulse(cr_fp_t pulseUs);
virtual bool handlePulse();
virtual void updateLcd();
Expand All @@ -44,16 +41,18 @@ class CRIO {
virtual bool percussionEnabled();
virtual bool fixedPulseEnabled();
bool scheduled;
bool slipTick;
cr_fp_t pw;
uint8_t maxPitch;
cr_fp_t breakoutUs;
private:
bool handleShortPulse();
bool handleLongPulse();
bool handleNoPulse();
void pulseOff();
void pulseOn();
bool _pulseState;
cr_pulse_t _oneShotPulseUs;
uint8_t _multiShotPulses;
bool (CRIO::*handlePulsePtr)(void);
cr_pulse_t _remainingPulseUs;
uint16_t _ticksSinceLastPulse;
};

Expand Down
1 change: 1 addition & 0 deletions CRMidi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ void CRMidi::handleControlChange(byte channel, byte number, byte value) {
break;
case 121: // Reset All Controllers
midiChannel->ResetCC();
requireRetune = true;
break;
case 95:
// Set channel detune of 2nd oscillator in cents.
Expand Down
5 changes: 3 additions & 2 deletions Oscillator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ inline void Oscillator::_updateNextClock(cr_tick_t newNextClock) {
}

cr_tick_t Oscillator::TicksUntilTriggered(cr_tick_t masterClock, cr_tick_t clockRemainder) {
if (masterClock <= _nextClock) {
if (_nextClock >= masterClock) {
return _nextClock - masterClock;
}
return _nextClock + clockRemainder;
// account for 0th tick on rollover.
return _nextClock + (clockRemainder + 1);
}

bool Oscillator::Triggered(cr_tick_t masterClock) {
Expand Down
14 changes: 8 additions & 6 deletions OscillatorController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ OscillatorController::OscillatorController() {
_controlClock = 0;
_lfoClock = 0;
_reschedulePending = false;
audibleOscillator = NULL;
controlTriggered = true;
tremoloLfo = _lfos;
vibratoLfo = _lfos + 1;
Expand Down Expand Up @@ -78,7 +79,7 @@ void OscillatorController::RestartLFOs() {
FOR_ALL_LFO(lfo->Restart());
}

void OscillatorController::Reschedule(Oscillator **audibleOscillator) {
void OscillatorController::_Reschedule() {
cr_tick_t minNextTriggeredTicks = masterClockMax;
cr_tick_t clockRemainder = masterClockMax - _masterClock;
FOR_ALL_OSC(
Expand All @@ -87,8 +88,8 @@ void OscillatorController::Reschedule(Oscillator **audibleOscillator) {
nextTriggeredTicks = oscillator->SetNextTick(_masterClock);
if (oscillator->audible) {
// Always pick the highest frequency audible oscillator.
if (*audibleOscillator == NULL || (oscillator->hz > (*audibleOscillator)->hz)) {
*audibleOscillator = oscillator;
if (audibleOscillator == NULL || (oscillator->hz > audibleOscillator->hz)) {
audibleOscillator = oscillator;
}
}
}
Expand All @@ -100,10 +101,11 @@ void OscillatorController::Reschedule(Oscillator **audibleOscillator) {
_reschedulePending = false;
}

bool OscillatorController::Triggered(Oscillator **audibleOscillator) {
*audibleOscillator = NULL;
bool OscillatorController::Triggered() {
Tick();
audibleOscillator = NULL;
if (_reschedulePending || _nextTriggeredOscillator->Triggered(_masterClock)) {
Reschedule(audibleOscillator);
_Reschedule();
return true;
}
return false;
Expand Down
5 changes: 3 additions & 2 deletions OscillatorController.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class OscillatorController {
void Tick();
void RestartLFOs();
void ResetAll();
bool Triggered(Oscillator **audibleOscillator);
bool Triggered();
Oscillator *GetFreeOscillator();
void ReturnFreeOscillator(Oscillator *oscillator);
void SetMaxHz(cr_fp_t newMaxHz);
Expand All @@ -31,8 +31,9 @@ class OscillatorController {
Lfo *tremoloLfo;
Lfo *vibratoLfo;
Lfo *configurableLfo;
Oscillator *audibleOscillator;
private:
void Reschedule(Oscillator **audibleOscillator);
void _Reschedule();
Lfo _lfos[lfoCount];
Oscillator _oscillators[oscillatorCount];
cr_fp_t _maxHz;
Expand Down
35 changes: 18 additions & 17 deletions chime_red2.ino
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, CR_SERIAL, MIDI, ChimeRedSettings);
CR_IO crio;
OscillatorController oc;
CRMidi crmidi(&oc, &crio);
void (*isrPtr)(void) = &nextISR;

inline void handleNoteOn(byte channel, byte note, byte velocity) {
crmidi.handleNoteOn(channel, note, velocity);
Expand All @@ -78,26 +79,22 @@ inline void resetAll() {
crmidi.ResetAll();
}

void masterISR() {
bool remainderPulse = crio.handlePulse();
if (remainderPulse) {
crio.slipTick = true;
return;
}
Oscillator *audibleOscillator = NULL;
oc.Tick();
if (crio.slipTick) {
oc.Triggered(&audibleOscillator);
oc.Tick();
oc.Triggered(&audibleOscillator);
crio.slipTick = false;
void slipTickISR() {
oc.Triggered();
oc.Triggered();
isrPtr = &nextISR;
}

void nextISR() {
// This ISR period was used to output pulse less than the ISR period - catch up on next ISR.
if (crio.handlePulse()) {
isrPtr = &slipTickISR;
return;
}
if (oc.Triggered(&audibleOscillator)) {
if (audibleOscillator) {
cr_fp_t p = crmidi.Modulate(audibleOscillator);
if (oc.Triggered()) {
if (oc.audibleOscillator) {
cr_fp_t p = crmidi.Modulate(oc.audibleOscillator);
crio.schedulePulse(p);
crio.startPulse();
}
return;
}
Expand All @@ -109,6 +106,10 @@ void masterISR() {
}
}

void masterISR() {
(*isrPtr)();
}

void enableMidi() {
START_ISR(masterClockHz, masterISR);
MIDI.setHandleNoteOn(handleNoteOn);
Expand Down
2 changes: 1 addition & 1 deletion types.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
#include <FixedPointsCommon.h>

typedef uint16_t cr_pulse_t;
typedef uint16_t cr_tick_t;
typedef uint32_t cr_tick_t;
typedef uint32_t cr_slowtick_t;
typedef SFixed<16, 15> cr_fp_t;

Expand Down

0 comments on commit 23f18ac

Please sign in to comment.