Add Channel-Portamento support

This commit is contained in:
Jack Andersen 2016-06-01 11:17:16 -10:00
parent d132b1be34
commit a047b1f6c8
6 changed files with 79 additions and 11 deletions

View File

@ -52,6 +52,7 @@ class Sequencer : public Entity
/** Voices corresponding to currently-pressed keys in channel */ /** Voices corresponding to currently-pressed keys in channel */
std::unordered_map<uint8_t, std::shared_ptr<Voice>> m_chanVoxs; std::unordered_map<uint8_t, std::shared_ptr<Voice>> m_chanVoxs;
std::unordered_set<std::shared_ptr<Voice>> m_keyoffVoxs; std::unordered_set<std::shared_ptr<Voice>> m_keyoffVoxs;
std::weak_ptr<Voice> m_lastVoice;
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */ int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */ float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
int8_t m_curProgram = 0; /**< MIDI program number */ int8_t m_curProgram = 0; /**< MIDI program number */

View File

@ -145,9 +145,9 @@ class SoundMacroState
uint8_t m_midiSustain; /**< Sustain MIDI controller */ uint8_t m_midiSustain; /**< Sustain MIDI controller */
uint8_t m_midiRelease; /**< Release MIDI controller */ uint8_t m_midiRelease; /**< Release MIDI controller */
uint8_t m_portamentoMode; /**< (0: Off, 1: On, 2: MIDI specified) */ uint8_t m_portamentoMode = 2; /**< (0: Off, 1: On, 2: MIDI specified) */
uint8_t m_portamentoType; /**< (0: New key pressed while old key pressed, 1: Always) */ uint8_t m_portamentoType = 0; /**< (0: New key pressed while old key pressed, 1: Always) */
float m_portamentoTime; /**< portamento transition time, 0.f will perform legato */ float m_portamentoTime = 0.5f; /**< portamento transition time, 0.f will perform legato */
/** Used to build a multi-component formula for overriding controllers */ /** Used to build a multi-component formula for overriding controllers */
struct Evaluator struct Evaluator

View File

@ -96,6 +96,9 @@ class Voice : public Entity
Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */ Envelope m_pitchAdsr; /**< Pitch envelope for SETPITCHADSR */
int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */ int32_t m_pitchEnvRange; /**< Pitch delta for SETPITCHADSR (in cents) */
float m_portamentoTime; /**< time since last portamento invocation, -1 for no active portamento-glide */
int32_t m_portamentoTarget; /**< destination pitch for latest portamento invocation */
uint32_t m_pitchSweep1; /**< Current value of PITCHSWEEP1 controller (in cents) */ uint32_t m_pitchSweep1; /**< Current value of PITCHSWEEP1 controller (in cents) */
uint32_t m_pitchSweep2; /**< Current value of PITCHSWEEP2 controller (in cents) */ uint32_t m_pitchSweep2; /**< Current value of PITCHSWEEP2 controller (in cents) */
int16_t m_pitchSweep1Add; /**< Value to add to PITCHSWEEP1 controller each cycle */ int16_t m_pitchSweep1Add; /**< Value to add to PITCHSWEEP1 controller each cycle */
@ -275,6 +278,9 @@ public:
/** Get note played on voice */ /** Get note played on voice */
uint8_t getLastNote() const {return m_state.m_initKey;} uint8_t getLastNote() const {return m_state.m_initKey;}
/** Do portamento glide; returns `false` if portamento disabled */
bool doPortamento(uint8_t newNote);
/** Get MIDI Controller value on voice */ /** Get MIDI Controller value on voice */
int8_t getCtrlValue(uint8_t ctrl) const int8_t getCtrlValue(uint8_t ctrl) const
{ {

View File

@ -135,10 +135,24 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
if (!m_page) if (!m_page)
return {}; return {};
/* If portamento is enabled for voice, pre-empt spawning new voices */
if (std::shared_ptr<Voice> lastVoice = m_lastVoice.lock())
{
uint8_t lastNote = lastVoice->getLastNote();
if (lastVoice->doPortamento(note))
{
m_chanVoxs.erase(lastNote);
m_chanVoxs[note] = lastVoice;
return lastVoice;
}
}
/* Ensure keyoff sent first */ /* Ensure keyoff sent first */
auto keySearch = m_chanVoxs.find(note); auto keySearch = m_chanVoxs.find(note);
if (keySearch != m_chanVoxs.cend()) if (keySearch != m_chanVoxs.cend())
{ {
if (keySearch->second == m_lastVoice.lock())
m_lastVoice.reset();
keySearch->second->keyOff(); keySearch->second->keyOff();
keySearch->second->setPedal(false); keySearch->second->setPedal(false);
m_keyoffVoxs.emplace(std::move(keySearch->second)); m_keyoffVoxs.emplace(std::move(keySearch->second));
@ -167,6 +181,8 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
if (m_ctrlVals[64] > 64) if (m_ctrlVals[64] > 64)
(*ret)->setPedal(true); (*ret)->setPedal(true);
m_lastVoice = *ret;
} }
return *ret; return *ret;
@ -189,6 +205,8 @@ void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
if (keySearch == m_chanVoxs.cend()) if (keySearch == m_chanVoxs.cend())
return; return;
if (keySearch->second == m_lastVoice.lock())
m_lastVoice.reset();
keySearch->second->keyOff(); keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second)); m_keyoffVoxs.emplace(std::move(keySearch->second));
m_chanVoxs.erase(keySearch); m_chanVoxs.erase(keySearch);
@ -303,6 +321,8 @@ void Sequencer::ChannelState::allOff()
{ {
for (auto it = m_chanVoxs.begin() ; it != m_chanVoxs.end() ;) for (auto it = m_chanVoxs.begin() ; it != m_chanVoxs.end() ;)
{ {
if (it->second == m_lastVoice.lock())
m_lastVoice.reset();
it->second->keyOff(); it->second->keyOff();
m_keyoffVoxs.emplace(std::move(it->second)); m_keyoffVoxs.emplace(std::move(it->second));
it = m_chanVoxs.erase(it); it = m_chanVoxs.erase(it);
@ -355,6 +375,8 @@ void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
Voice* vox = it->second.get(); Voice* vox = it->second.get();
if (vox->m_keygroup == kg) if (vox->m_keygroup == kg)
{ {
if (it->second == m_lastVoice.lock())
m_lastVoice.reset();
if (now) if (now)
{ {
it = m_chanVoxs.erase(it); it = m_chanVoxs.erase(it);

View File

@ -135,7 +135,8 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick
m_loopCountdown = -1; m_loopCountdown = -1;
m_lastPlayMacroVid = -1; m_lastPlayMacroVid = -1;
m_useAdsrControllers = false; m_useAdsrControllers = false;
m_portamentoMode = 0; m_portamentoMode = 2;
m_portamentoTime = 0.5f;
m_header = *reinterpret_cast<const Header*>(ptr); m_header = *reinterpret_cast<const Header*>(ptr);
if (swapData) if (swapData)
m_header.swapBig(); m_header.swapBig();

View File

@ -52,6 +52,7 @@ void Voice::_reset()
m_pitchSweep2 = 0; m_pitchSweep2 = 0;
m_pitchSweep2Times = 0; m_pitchSweep2Times = 0;
m_pitchSweep2It = 0; m_pitchSweep2It = 0;
m_portamentoTime = -1.f;
m_envelopeTime = -1.f; m_envelopeTime = -1.f;
m_panningTime = -1.f; m_panningTime = -1.f;
m_spanningTime = -1.f; m_spanningTime = -1.f;
@ -333,9 +334,24 @@ bool Voice::_advanceSample(int16_t& samp, int32_t& newPitch)
newPitch = m_curPitch; newPitch = m_curPitch;
refresh |= m_pitchDirty; refresh |= m_pitchDirty;
m_pitchDirty = false; m_pitchDirty = false;
if (m_portamentoTime >= 0.f)
{
m_portamentoTime += dt;
float t = std::max(0.f, std::min(1.f, m_portamentoTime / m_state.m_portamentoTime));
newPitch = (m_curPitch * (1.0f - t)) + (m_portamentoTarget * t);
refresh = true;
/* Done with portamento */
if (m_portamentoTime > m_state.m_portamentoTime)
{
m_portamentoTime = -1.f;
m_curPitch = m_portamentoTarget;
}
}
if (m_pitchEnv) if (m_pitchEnv)
{ {
newPitch = m_curPitch * m_pitchAdsr.advance(dt); newPitch *= m_pitchAdsr.advance(dt);
refresh = true; refresh = true;
} }
@ -865,7 +881,7 @@ void Voice::startFadeIn(double dur, float vol, const Curve* envCurve)
void Voice::startPanning(double dur, uint8_t panPos, int8_t panWidth) void Voice::startPanning(double dur, uint8_t panPos, int8_t panWidth)
{ {
m_panningTime = m_voiceTime; m_panningTime = 0.f;
m_panningDur = dur; m_panningDur = dur;
m_panPos = panPos; m_panPos = panPos;
m_panWidth = panWidth; m_panWidth = panWidth;
@ -873,7 +889,7 @@ void Voice::startPanning(double dur, uint8_t panPos, int8_t panWidth)
void Voice::startSpanning(double dur, uint8_t spanPos, int8_t spanWidth) void Voice::startSpanning(double dur, uint8_t spanPos, int8_t spanWidth)
{ {
m_spanningTime = m_voiceTime; m_spanningTime = 0.f;
m_spanningDur = dur; m_spanningDur = dur;
m_spanPos = spanPos; m_spanPos = spanPos;
m_spanWidth = spanWidth; m_spanWidth = spanWidth;
@ -1015,6 +1031,32 @@ void Voice::setAftertouch(uint8_t aftertouch)
vox->setAftertouch(aftertouch); vox->setAftertouch(aftertouch);
} }
bool Voice::doPortamento(uint8_t newNote)
{
bool pState;
switch (m_state.m_portamentoMode)
{
case 0:
default:
pState = false;
break;
case 1:
pState = true;
break;
case 2:
pState = getCtrlValue(65) >= 64;
break;
}
if (!pState)
return false;
m_portamentoTime = 0.f;
m_portamentoTarget = newNote * 100;
m_state.m_initKey = newNote;
return true;
}
void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val) void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
{ {
if (ctrl == 0x40) if (ctrl == 0x40)
@ -1024,10 +1066,6 @@ void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
else else
setPedal(false); setPedal(false);
} }
else if (ctrl == 0x41)
{
printf("PORTAMENTO %d\n", val);
}
else if (ctrl == 0x5b) else if (ctrl == 0x5b)
{ {
setReverbVol(val / 127.f); setReverbVol(val / 127.f);