Skip to content
This repository has been archived by the owner on Oct 16, 2021. It is now read-only.

N163 Zxx

nyanpasu64 edited this page Jul 2, 2018 · 6 revisions

Summary: CChannelHandler::HandleNoteData

The order CChannelHandler::HandleNoteData processes events on the same row (https://github.com/nyanpasu64/j0CC-FamiTracker/wiki/Effects):

  • Effect: CChannelHandler::HandleEffect(name, param)
  • Note: CChannelHandler::HandleEmptyNote(), HandleCut(), HandleRelease(), HandleNote(note, octave)
  • Instrument: CChannelHandler::HandleInstrument(isTrigger, isNewInstr)

What order should events be processed?

  • 3xx on note row: effect before note
  • DPCM pitch: note before instr
  • N163 Zxx bounds checking: instr before effect (not satisfied, causing Zxx to bounds-check based on the instrument used 1 row above)
    • I have removed this dependency by removing Zxx bounds checking.

hooray, circular dependencies.

I may move Zxx into a new "handle effect after instrument" method, also handling 1xx and 2xx. I ended up removing Zxx bounds checking.

Test Case: n163-zxx-validation.zip

Zxx effect: Instrument wavelength bounds-checking

void CSeqInstHandlerN163::LoadInstrument(std::shared_ptr<CInstrument> pInst) {
    pInterface->SetWaveLength(pN163Inst->GetWaveSize());
    pInterface->SetWavePosition(pN163Inst->GetWavePos());
    pInterface->SetWaveCount(pN163Inst->GetWaveCount());
    RequestWaveUpdate();
}

void CChannelHandlerN163::SetWaveLength(int Length) {
    m_iWaveLen = Length;
}

bool CChannelHandlerN163::HandleEffect(effect_t EffNum, unsigned char EffParam) {
    switch (EffNum) {
    case EF_N163_WAVE_BUFFER:
        if (EffParam == 0x7F) {
            m_iWavePos = m_iWavePosOld;
            m_bDisableLoad = false;
        } else {
            if (EffParam + (m_iWaveLen >> 1) > 0x80 - 8 * m_iChannels) break;
            m_iWavePos = EffParam << 1;
            m_bDisableLoad = true;
        }
        if (auto pHandler = dynamic_cast<CSeqInstHandlerN163*>(m_pInstHandler.get()))
            pHandler->RequestWaveUpdate();
        break;
    }
    return true;
}

Function Calls

HertzDevil release build :(

CSoundDriver::Tick() Line 166
CSoundDriver::UpdateChannels() Line 234
CSoundDriver::ForeachTrack(CSoundDriver::UpdateChannels::__l2::void <lambda>(CChannelHandler &, CTrackerChannel &, stChannelID) f) Line 98
CSoundDriver::UpdateChannels::__l2::<lambda>(CChannelHandler & Chan, CTrackerChannel & TrackerChan, stChannelID ID) Line 247
CChannelHandler::PlayNote(stChanNote NoteData) Line 278
	CChannelHandler::HandleNoteData(stChanNote & NoteData) Line 365
	CChannelHandlerN163::HandleEffect(effect_t EffNum, unsigned char EffParam) Line 89

	CChannelHandler::HandleNoteData(stChanNote & NoteData) Line 422
	CChannelHandlerN163::HandleInstrument(bool Trigger, bool NewInstrument) Line 104
	CChannelHandler::HandleInstrument(bool Trigger, bool NewInstrument) Line 444
	CSeqInstHandlerN163::LoadInstrument(std::shared_ptr<CInstrument> pInst) Line 46

HertzDevil da6767c:

CSoundGen::BeginPlayer(std::unique_ptr<CPlayerCursor,std::default_delete<CPlayerCursor> > Pos) Line 610
CSoundGen::ApplyGlobalState() Line 624
CSoundDriver::LoadSoundState(const CSongState & state) Line 200
CSoundDriver::ForeachTrack<void <lambda>(CChannelHandler &, CTrackerChannel &, chan_id_t) >(CSoundDriver::LoadSoundState::__l2::void <lambda>(CChannelHandler &, CTrackerChannel &, chan_id_t) f) Line 111
CSoundDriver::LoadSoundState::__l2::<lambda>(CChannelHandler & ch, CTrackerChannel & tr, chan_id_t id) Line 198
	CChannelHandler::ApplyChannelState(const stChannelState & State) Line 209
	CChannelHandlerN163::HandleInstrument(bool Trigger, bool NewInstrument) Line 100
	CChannelHandler::HandleInstrument(bool Trigger, bool NewInstrument) Line 448
	CSeqInstHandlerN163::LoadInstrument(std::shared_ptr<CInstrument> pInst) Line 46

	CChannelHandler::ApplyChannelState(const stChannelState & State) Line 214
	CChannelHandlerN163::HandleEffect(effect_t EffNum, unsigned char EffParam) Line 85

CSoundDriver::Tick() Line 211
CSoundDriver::UpdateChannels() Line 298
CSoundDriver::ForeachTrack<void <lambda>(CChannelHandler &, CTrackerChannel &, chan_id_t) >(CSoundDriver::UpdateChannels::__l2::void <lambda>(CChannelHandler &, CTrackerChannel &, chan_id_t) f) Line 111
CSoundDriver::UpdateChannels::__l2::<lambda>(CChannelHandler & Chan, CTrackerChannel & TrackerChan, chan_id_t ID) Line 288
CChannelHandler::PlayNote(stChanNote NoteData) Line 281
CChannelHandler::HandleNoteData(stChanNote & NoteData) Line 366
CChannelHandlerN163::HandleEffect(effect_t EffNum, unsigned char EffParam) Line 85

The first 2 calls do not occur on N163 Zxx Validation (Row 4).0cc and the bug reappears.

CChannelHandler::HandleNoteData()

  • (checks if note exists, pushes to echo buffer)
  • HandleEffect
  • Write volume
  • (calculates instrument #)
  • m_iNote = RunNote(...)
  • HandleInstrument.
switch (pNoteData->Note) {		// // // set note value before loading instrument
case NONE: case HALT: case RELEASE: break;
default: m_iNote = RunNote(pNoteData->Octave, pNoteData->Note);
}

DPCM: Handle notes before instruments.

CChannelHandler::HandleInstrument calls CDPCMChan::HandleNoteData and CChannelHandler::HandleInstrument.

  • CDPCMChan::HandleNoteData
    • m_iCustomPitch = -1;
  • CChannelHandler::HandleInstrument calls CDPCMChan::PlaySample
    • m_iPeriod = m_iCustomPitch != -1 ? m_iCustomPitch : Pitch;

So notes must be handled before instruments.

3xx: Handling effects before notes

SetupSlide() touches m_iNote:

  • Effect 3xx begins before current note!
  • CChannelHandler::HandleEffect with EF_PORTAMENTO:
    • SetupSlide() depends on m_iNote.
    • HandleNoteData() assigns m_iNote after HandleEffect()
  • m_iNote = RunNote(pNoteData->Octave, pNoteData->Note);
  • EF_SLIDE_DOWN and EF_SLIDE_UP
    • SetupSlide() is called specifically after m_iNote is assigned.

Extra: What touches m_iNote?

  • GetNote()
    • CInstHandlerDPCM
      • TriggerInstrument(): void
    • CSeqInstHandler
      • ProcessSequence(int, unsigned, int): bool
      • UpdateInstrument(): void
  • CChannelHandler
    • CChannelHandler(int, int)
      • (57,2) m_iNote(0),
    • GetNote() const: int
      • (981,9) return m_iNote;
    • GetPitch() const: int
      • (106,23) if (m_iPitch != 0 && m_iNote != 0 && m_pNoteLookupTable != NULL) {
      • (108,27) int LowNote = std::max(m_iNote - PITCH_WHEEL_RANGE, 0);
      • (109,27) int HighNote = std::min(m_iNote + PITCH_WHEEL_RANGE, 95);
      • (110,34) int Freq = m_pNoteLookupTable[m_iNote];
    • HandleNoteData(stChanNote*, int): void
      • (402,11) default: m_iNote = RunNote(pNoteData->Octave, pNoteData->Note);
    • ResetChannel(): void
      • (149,2) m_iNote = 0; // // //
    • SetNote(int): void
      • (976,2) m_iNote = Note;
    • SetupSlide(): void
      • (546,29) m_iPortaTo = TriggerNote(m_iNote);
      • (549,3) m_iNote = m_iNote + (m_iEffectParam & 0xF);
      • (549,13) m_iNote = m_iNote + (m_iEffectParam & 0xF);
      • (551,28) m_iPortaTo = TriggerNote(m_iNote);
      • (554,3) m_iNote = m_iNote - (m_iEffectParam & 0xF);
      • (554,13) m_iNote = m_iNote - (m_iEffectParam & 0xF);
      • (556,28) m_iPortaTo = TriggerNote(m_iNote);
    • UpdateEffects(): void
      • (765,29) SetPeriod(TriggerNote(m_iNote));
      • (768,29) SetPeriod(TriggerNote(m_iNote + (m_iEffectParam >> 4)));
      • (773,29) SetPeriod(TriggerNote(m_iNote + (m_iEffectParam & 0x0F)));
    • UpdateTranspose(): void
      • (705,11) SetNote(m_iNote + m_iTransposeTarget * (m_bTransposeDown ? -1 : 1));
      • (706,25) SetPeriod(TriggerNote(m_iNote));
  • CChannelHandlerN163
    • HandleCut(): void
      • (120,2) m_iNote = 0;
  • CChannelHandlerS5B
    • HandleCut(): void
      • (151,2) m_iNote = 0;
  • CDPCMChan
    • HandleNote(int, int): void
      • (622,2) m_iNote = MIDI_NOTE(Octave, Note); // // //
      • (623,14) TriggerNote(m_iNote);
  • CNoiseChan
    • HandleNote(int, int): void
      • (393,2) m_iNote = NewNote;
    • SetupSlide(): void
      • (405,3) m_iNote += (m_iEffectParam & 0xF);
      • (409,3) m_iNote -= (m_iEffectParam & 0xF);
      • (416,19) RegisterKeyState(m_iNote);
      • (417,15) m_iPortaTo = m_iNote;
  • CVRC7Channel
    • ClearRegisters(): void
      • (329,2) m_iNote = 0;