N64 SNG fixes

This commit is contained in:
Jack Andersen 2016-06-19 22:08:32 -10:00
parent 7666b51c50
commit 3bc47baa1d
6 changed files with 68 additions and 77 deletions

View File

@ -41,9 +41,9 @@ class SongState
uint32_t m_startTick; uint32_t m_startTick;
uint16_t m_unk1; uint16_t m_unk1;
uint16_t m_unk2; uint16_t m_unk2;
uint16_t m_regionIndex; int16_t m_regionIndex;
int16_t m_initialPitch; int16_t m_unk3;
void swapBig(); bool indexValid() const;
}; };
/** Tempo change entry */ /** Tempo change entry */
@ -56,8 +56,8 @@ class SongState
const unsigned char* m_songData = nullptr; /**< Base pointer to active song */ const unsigned char* m_songData = nullptr; /**< Base pointer to active song */
/** State of a single channel within arrangement */ /** State of a single track within arrangement */
struct Channel struct Track
{ {
struct Header struct Header
{ {
@ -79,17 +79,17 @@ class SongState
int32_t m_lastPitchVal = 0; /**< Last value of pitch */ int32_t m_lastPitchVal = 0; /**< Last value of pitch */
uint32_t m_lastModTick = 0; /**< Last position of mod wheel change */ uint32_t m_lastModTick = 0; /**< Last position of mod wheel change */
int32_t m_lastModVal = 0; /**< Last value of mod */ int32_t m_lastModVal = 0; /**< Last value of mod */
std::array<uint16_t, 128> m_remNoteLengths = {}; /**< Remaining ticks per note */ std::array<int, 128> m_remNoteLengths; /**< Remaining ticks per note */
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */ int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
int32_t m_lastN64EventTick = 0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */ int32_t m_lastN64EventTick = 0; /**< Last command time on this channel (for computing delta times from absolute times in N64 songs) */
Channel(SongState& parent, uint8_t midiChan, const TrackRegion* regions); Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions);
void setRegion(Sequencer& seq, const TrackRegion* region); void setRegion(Sequencer& seq, const TrackRegion* region);
void advanceRegion(Sequencer& seq); void advanceRegion(Sequencer& seq);
bool advance(Sequencer& seq, int32_t ticks); bool advance(Sequencer& seq, int32_t ticks);
}; };
std::array<std::experimental::optional<Channel>, 64> m_channels; std::array<std::experimental::optional<Track>, 64> m_tracks;
const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */ const uint32_t* m_regionIdx; /**< Table of offsets to song-region data */
/** Current pointer to tempo control, iterated over playback */ /** Current pointer to tempo control, iterated over playback */

View File

@ -308,12 +308,6 @@ public:
_notifyCtrlChange(ctrl, val); _notifyCtrlChange(ctrl, val);
} }
/** Get ModWheel value on voice */
int8_t getModWheel() const
{
return m_state.m_modWheelSel ? m_state.m_modWheelSel.evaluate(*this, m_state) : getCtrlValue(1);
}
/** 'install' external MIDI controller storage */ /** 'install' external MIDI controller storage */
void installCtrlValues(int8_t* cvs) void installCtrlValues(int8_t* cvs)
{ {
@ -322,7 +316,7 @@ public:
} }
/** Get MIDI pitch wheel value on voice */ /** Get MIDI pitch wheel value on voice */
int8_t getPitchWheel() const {return m_curPitchWheel * 127 / 2 + 64;} float getPitchWheel() const {return m_curPitchWheel;}
/** Get MIDI aftertouch value on voice */ /** Get MIDI aftertouch value on voice */
int8_t getAftertouch() const {return m_curAftertouch;} int8_t getAftertouch() const {return m_curAftertouch;}

View File

@ -72,15 +72,15 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
m_midiSetup = it->second->data(); m_midiSetup = it->second->data();
m_submix = m_engine.addSubmix(smx); m_submix = m_engine.addSubmix(smx);
m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f); m_submix->makeReverbHi(0.2f, 0.3f, 1.f, 0.5f, 0.f, 0.f);
} }
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId,
const SFXGroupIndex* sfxGroup, Submix* smx) const SFXGroupIndex* sfxGroup, Submix* smx)
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup) : Entity(engine, group, groupId), m_sfxGroup(sfxGroup)
{ {
m_submix = m_engine.addSubmix(smx); //m_submix = m_engine.addSubmix(smx);
m_submix->makeReverbHi(0.2f, 0.65f, 1.f, 0.5f, 0.f, 0.f); //m_submix->makeReverbHi(0.2f, 0.3f, 1.f, 0.5f, 0.f, 0.f);
std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX; std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX;
for (const auto& sfx : sfxGroup->m_sfxEntries) for (const auto& sfx : sfxGroup->m_sfxEntries)

View File

@ -76,13 +76,9 @@ void SongState::Header::swapBig()
m_unkOff = SBig(m_unkOff); m_unkOff = SBig(m_unkOff);
} }
void SongState::TrackRegion::swapBig() bool SongState::TrackRegion::indexValid() const
{ {
m_startTick = SBig(m_startTick); return SBig(m_regionIndex) >= 0;
m_unk1 = SBig(m_unk1);
m_unk2 = SBig(m_unk2);
m_regionIndex = SBig(m_regionIndex);
m_initialPitch = SBig(m_initialPitch);
} }
void SongState::TempoChange::swapBig() void SongState::TempoChange::swapBig()
@ -91,20 +87,22 @@ void SongState::TempoChange::swapBig()
m_tempo = SBig(m_tempo); m_tempo = SBig(m_tempo);
} }
void SongState::Channel::Header::swapBig() void SongState::Track::Header::swapBig()
{ {
m_type = SBig(m_type); m_type = SBig(m_type);
m_pitchOff = SBig(m_pitchOff); m_pitchOff = SBig(m_pitchOff);
m_modOff = SBig(m_modOff); m_modOff = SBig(m_modOff);
} }
SongState::Channel::Channel(SongState& parent, uint8_t midiChan, const TrackRegion* regions) SongState::Track::Track(SongState& parent, uint8_t midiChan, const TrackRegion* regions)
: m_parent(parent), m_midiChan(midiChan), m_curRegion(nullptr), m_nextRegion(regions) : m_parent(parent), m_midiChan(midiChan), m_curRegion(nullptr), m_nextRegion(regions)
{}
void SongState::Channel::setRegion(Sequencer& seq, const TrackRegion* region)
{ {
assert(region->m_regionIndex != 0xffff); for (int i=0 ; i<128 ; ++i)
m_remNoteLengths[i] = INT_MIN;
}
void SongState::Track::setRegion(Sequencer& seq, const TrackRegion* region)
{
m_curRegion = region; m_curRegion = region;
uint32_t regionIdx = SBig(m_curRegion->m_regionIndex); uint32_t regionIdx = SBig(m_curRegion->m_regionIndex);
m_nextRegion = &m_curRegion[1]; m_nextRegion = &m_curRegion[1];
@ -113,7 +111,6 @@ void SongState::Channel::setRegion(Sequencer& seq, const TrackRegion* region)
Header header = *reinterpret_cast<const Header*>(m_data); Header header = *reinterpret_cast<const Header*>(m_data);
header.swapBig(); header.swapBig();
assert(header.m_type == 8);
m_data += 12; m_data += 12;
if (header.m_pitchOff) if (header.m_pitchOff)
@ -123,7 +120,6 @@ void SongState::Channel::setRegion(Sequencer& seq, const TrackRegion* region)
m_eventWaitCountdown = 0; m_eventWaitCountdown = 0;
m_lastPitchTick = m_parent.m_curTick; m_lastPitchTick = m_parent.m_curTick;
//m_lastPitchVal = SBig(m_curRegion->m_initialPitch);
m_lastPitchVal = 0; m_lastPitchVal = 0;
seq.setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f)); seq.setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f));
m_lastModTick = m_parent.m_curTick; m_lastModTick = m_parent.m_curTick;
@ -140,7 +136,7 @@ void SongState::Channel::setRegion(Sequencer& seq, const TrackRegion* region)
} }
} }
void SongState::Channel::advanceRegion(Sequencer& seq) void SongState::Track::advanceRegion(Sequencer& seq)
{ {
setRegion(seq, m_nextRegion); setRegion(seq, m_nextRegion);
} }
@ -154,16 +150,16 @@ void SongState::initialize(const unsigned char* ptr)
m_regionIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_regionIdxOff); m_regionIdx = reinterpret_cast<const uint32_t*>(ptr + m_header.m_regionIdxOff);
const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff); const uint8_t* chanMap = reinterpret_cast<const uint8_t*>(ptr + m_header.m_chanMapOff);
/* Initialize all channels */ /* Initialize all tracks */
for (int i=0 ; i<64 ; ++i) for (int i=0 ; i<64 ; ++i)
{ {
if (trackIdx[i]) if (trackIdx[i])
{ {
const TrackRegion* region = reinterpret_cast<const TrackRegion*>(ptr + SBig(trackIdx[i])); const TrackRegion* region = reinterpret_cast<const TrackRegion*>(ptr + SBig(trackIdx[i]));
m_channels[i].emplace(*this, chanMap[i], region); m_tracks[i].emplace(*this, chanMap[i], region);
} }
else else
m_channels[i] = std::experimental::nullopt; m_tracks[i] = std::experimental::nullopt;
} }
/* Initialize tempo */ /* Initialize tempo */
@ -177,12 +173,12 @@ void SongState::initialize(const unsigned char* ptr)
m_songState = SongPlayState::Playing; m_songState = SongPlayState::Playing;
} }
bool SongState::Channel::advance(Sequencer& seq, int32_t ticks) bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
{ {
int32_t endTick = m_parent.m_curTick + ticks; int32_t endTick = m_parent.m_curTick + ticks;
/* Advance region if needed */ /* Advance region if needed */
while (m_nextRegion->m_regionIndex != 0xffff) while (m_nextRegion->indexValid())
{ {
uint32_t nextRegTick = SBig(m_nextRegion->m_startTick); uint32_t nextRegTick = SBig(m_nextRegion->m_startTick);
if (endTick > nextRegTick) if (endTick > nextRegTick)
@ -191,8 +187,22 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
break; break;
} }
/* Stop finished notes */
for (int i=0 ; i<128 ; ++i)
{
if (m_remNoteLengths[i] != INT_MIN)
{
m_remNoteLengths[i] -= ticks;
if (m_remNoteLengths[i] <= 0)
{
seq.keyOff(m_midiChan, i, 0);
m_remNoteLengths[i] = INT_MIN;
}
}
}
if (!m_data) if (!m_data)
return m_nextRegion->m_regionIndex == 0xffff; return !m_nextRegion->indexValid();
/* Update continuous pitch data */ /* Update continuous pitch data */
if (m_pitchWheelData) if (m_pitchWheelData)
@ -260,21 +270,6 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
} }
} }
/* Stop finished notes */
for (int i=0 ; i<128 ; ++i)
{
if (m_remNoteLengths[i])
{
if (m_remNoteLengths[i] <= ticks)
{
seq.keyOff(m_midiChan, i, 0);
m_remNoteLengths[i] = 0;
}
else
m_remNoteLengths[i] -= ticks;
}
}
/* Loop through as many commands as we can for this time period */ /* Loop through as many commands as we can for this time period */
if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58) if (m_parent.m_header.m_trackIdxOff == 0x18 || m_parent.m_header.m_trackIdxOff == 0x58)
{ {
@ -295,7 +290,7 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
{ {
/* End of channel */ /* End of channel */
m_data = nullptr; m_data = nullptr;
return m_nextRegion->m_regionIndex == 0xffff; return !m_nextRegion->indexValid();
} }
else if (m_data[0] & 0x80) else if (m_data[0] & 0x80)
{ {
@ -339,7 +334,7 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
{ {
/* End of channel */ /* End of channel */
m_data = nullptr; m_data = nullptr;
return m_nextRegion->m_regionIndex == 0xffff; return !m_nextRegion->indexValid();
} }
else if (m_data[0] & 0x80) else if (m_data[0] & 0x80)
{ {
@ -365,7 +360,6 @@ bool SongState::Channel::advance(Sequencer& seq, int32_t ticks)
/* Set next delta-time */ /* Set next delta-time */
int32_t absTick = SBig(*reinterpret_cast<const int32_t*>(m_data)); int32_t absTick = SBig(*reinterpret_cast<const int32_t*>(m_data));
assert(absTick >= m_lastN64EventTick);
m_eventWaitCountdown += absTick - m_lastN64EventTick; m_eventWaitCountdown += absTick - m_lastN64EventTick;
m_lastN64EventTick = absTick; m_lastN64EventTick = absTick;
m_data += 4; m_data += 4;
@ -412,10 +406,10 @@ bool SongState::advance(Sequencer& seq, double dt)
} }
} }
/* Advance all channels */ /* Advance all tracks */
for (std::experimental::optional<Channel>& chan : m_channels) for (std::experimental::optional<Track>& trk : m_tracks)
if (chan) if (trk)
done &= chan->advance(seq, remTicks); done &= trk->advance(seq, remTicks);
m_curTick += remTicks; m_curTick += remTicks;

View File

@ -52,21 +52,21 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
break; break;
case 129: case 129:
/* Aftertouch */ /* Aftertouch */
thisValue = vox.getAftertouch(); thisValue = vox.getAftertouch() * (2.f / 127.f);
break; break;
case 130: case 130:
/* LFO1 */ /* LFO1 */
if (vox.m_lfoPeriods[0]) if (vox.m_lfoPeriods[0])
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF) / 2.f + 0.5f) * 127.f; thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[0] * 2.f * M_PIF);
break; break;
case 131: case 131:
/* LFO2 */ /* LFO2 */
if (vox.m_lfoPeriods[1]) if (vox.m_lfoPeriods[1])
thisValue = (std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF) / 2.f + 0.5f) * 127.f; thisValue = std::sin(vox.m_voiceTime / vox.m_lfoPeriods[1] * 2.f * M_PIF);
break; break;
case 132: case 132:
/* Surround panning */ /* Surround panning */
thisValue = vox.m_curSpan * 64.f + 64.f; thisValue = vox.m_curSpan;
break; break;
case 133: case 133:
/* Macro-starting key */ /* Macro-starting key */
@ -78,10 +78,13 @@ float SoundMacroState::Evaluator::evaluate(const Voice& vox, const SoundMacroSta
break; break;
case 135: case 135:
/* Time since macro-start (ms) */ /* Time since macro-start (ms) */
thisValue = st.m_execTime * 1000.f; thisValue = clamp(0.f, float(st.m_execTime * 1000.f), 16383.f);
break; break;
default: default:
thisValue = vox.getCtrlValue(comp.m_midiCtrl); if (comp.m_midiCtrl == 10) /* Centered pan computation */
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f) - 1.f;
else
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f);
break; break;
} }
} }

View File

@ -253,12 +253,12 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
} }
/* Dynamically evaluate per-sample SoundMacro parameters */ /* Dynamically evaluate per-sample SoundMacro parameters */
float evalVol = m_state.m_volumeSel ? ((m_state.m_volumeSel.evaluate(*this, m_state) / 127.f) * m_curVol) : m_curVol; float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(*this, m_state) / 2.f * m_curVol) : m_curVol;
bool panDirty = false; bool panDirty = false;
if (m_state.m_panSel) if (m_state.m_panSel)
{ {
float evalPan = (m_state.m_panSel.evaluate(*this, m_state) - 64.f) / 64.f; float evalPan = m_state.m_panSel.evaluate(*this, m_state);
if (evalPan != m_curPan) if (evalPan != m_curPan)
{ {
m_curPan = evalPan; m_curPan = evalPan;
@ -267,7 +267,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
} }
if (m_state.m_spanSel) if (m_state.m_spanSel)
{ {
float evalSpan = (m_state.m_spanSel.evaluate(*this, m_state) - 64.f) / 64.f; float evalSpan = m_state.m_spanSel.evaluate(*this, m_state);
if (evalSpan != m_curSpan) if (evalSpan != m_curSpan)
{ {
m_curSpan = evalSpan; m_curSpan = evalSpan;
@ -276,7 +276,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
} }
if (m_state.m_reverbSel) if (m_state.m_reverbSel)
{ {
float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 127.f; float evalRev = m_state.m_reverbSel.evaluate(*this, m_state) / 2.f;
if (evalRev != m_curReverbVol) if (evalRev != m_curReverbVol)
{ {
m_curReverbVol = evalRev; m_curReverbVol = evalRev;
@ -287,7 +287,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
_setPan(m_curPan); _setPan(m_curPan);
if (m_state.m_pitchWheelSel) if (m_state.m_pitchWheelSel)
_setPitchWheel((m_state.m_pitchWheelSel.evaluate(*this, m_state) - 64.f) / 64.f); _setPitchWheel(m_state.m_pitchWheelSel.evaluate(*this, m_state));
/* Process user volume slew */ /* Process user volume slew */
if (m_engine.m_ampMode == AmplitudeMode::PerSample) if (m_engine.m_ampMode == AmplitudeMode::PerSample)
@ -326,11 +326,11 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
/* Apply tremolo */ /* Apply tremolo */
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale)) if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
{ {
float t = m_state.m_tremoloSel.evaluate(*this, m_state); float t = m_state.m_tremoloSel.evaluate(*this, m_state) / 2.f;
if (m_tremoloScale && m_tremoloModScale) if (m_tremoloScale && m_tremoloModScale)
{ {
float fac = (1.0f - t) + (m_tremoloScale * t); float fac = (1.0f - t) + (m_tremoloScale * t);
float modT = getModWheel() / 127.f; float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(*this, m_state) / 2.f) : (getCtrlValue(1) / 127.f);
float modFac = (1.0f - modT) + (m_tremoloModScale * modT); float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
m_nextLevel *= fac * modFac; m_nextLevel *= fac * modFac;
} }
@ -341,7 +341,7 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
} }
else if (m_tremoloModScale) else if (m_tremoloModScale)
{ {
float modT = getModWheel() / 127.f; float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(*this, m_state) / 2.f) : (getCtrlValue(1) / 127.f);
float modFac = (1.0f - modT) + (m_tremoloModScale * modT); float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
m_nextLevel *= modFac; m_nextLevel *= modFac;
} }
@ -451,7 +451,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data)
/* Process per-block evaluators here */ /* Process per-block evaluators here */
if (m_state.m_pedalSel) if (m_state.m_pedalSel)
{ {
bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 64; bool pedal = m_state.m_pedalSel.evaluate(*this, m_state) >= 1.f;
if (pedal != m_sustained) if (pedal != m_sustained)
setPedal(pedal); setPedal(pedal);
} }
@ -1110,7 +1110,7 @@ bool Voice::doPortamento(uint8_t newNote)
pState = true; pState = true;
break; break;
case 2: case 2:
pState = (m_state.m_portamentoSel ? m_state.m_portamentoSel.evaluate(*this, m_state) : getCtrlValue(65)) >= 64; pState = m_state.m_portamentoSel ? (m_state.m_portamentoSel.evaluate(*this, m_state) >= 1.f) : (getCtrlValue(65) >= 64);
break; break;
} }