Added Arp to TG menu

pull/764/head
jnonis 6 months ago
parent b2dcc29d73
commit 61f66848dc
  1. 79
      src/midi_arp.cpp
  2. 45
      src/midi_arp.h
  3. 96
      src/midi_effect_base.h
  4. 60
      src/minidexed.cpp
  5. 5
      src/minidexed.h
  6. 100
      src/uimenu.cpp
  7. 4
      src/uimenu.h

@ -1,11 +1,9 @@
#include "midi_arp.h"
#include <stdio.h>
MidiArp::MidiArp(float32_t samplerate, CDexedAdapter* synth)
MidiArp::MidiArp(float32_t samplerate, CDexedAdapter* synth) : MidiEffect(samplerate, synth)
{
this->samplerate = samplerate;
this->syncMode = 1;
this->synth = synth;
arpeggiator.transmitHostInfo(0, 4, 1, 1, 120.0);
arpeggiator.setSampleRate(samplerate);
@ -18,6 +16,79 @@ MidiArp::~MidiArp()
{
}
void MidiArp::setTempo(unsigned tempo)
{
arpeggiator.setBpm(tempo);
}
void MidiArp::setParameter(unsigned param, unsigned value)
{
switch (param)
{
case MidiArp::Param::BYPASS:
this->setBypass(value == 1);
break;
case MidiArp::Param::LATCH:
this->arpeggiator.setLatchMode(value == 1);
break;
case MidiArp::Param::SYNC:
this->arpeggiator.setSyncMode(value);
break;
case MidiArp::Param::ARP_MODE:
this->arpeggiator.setArpMode(value);
break;
case MidiArp::Param::DIVISION:
this->arpeggiator.setDivision(value);
break;
case MidiArp::Param::NOTE_LENGTH:
this->arpeggiator.setNoteLength(value);
break;
case MidiArp::Param::VELOCITY:
this->arpeggiator.setVelocity(value);
break;
case MidiArp::Param::OCTAVE_SPREAD:
this->arpeggiator.setOctaveSpread(value);
break;
case MidiArp::Param::OCTAVE_MODE:
this->arpeggiator.setOctaveMode(value);
break;
case MidiArp::Param::PANIC:
this->arpeggiator.setPanic(value == 1);
break;
default:
break;
}
}
unsigned MidiArp::getParameter(unsigned param)
{
switch (param)
{
case MidiArp::Param::BYPASS:
return this->getBypass() ? 1 : 0;
case MidiArp::Param::LATCH:
return this->arpeggiator.getLatchMode() ? 1 : 0;
case MidiArp::Param::SYNC:
return this->arpeggiator.getSyncMode();
case MidiArp::Param::ARP_MODE:
return this->arpeggiator.getArpMode();
case MidiArp::Param::DIVISION:
return this->arpeggiator.getDivision();
case MidiArp::Param::NOTE_LENGTH:
return this->arpeggiator.getNoteLength();
case MidiArp::Param::VELOCITY:
return this->arpeggiator.getVelocity();
case MidiArp::Param::OCTAVE_SPREAD:
return this->arpeggiator.getOctaveSpread();
case MidiArp::Param::OCTAVE_MODE:
return this->arpeggiator.getOctaveMode();
case MidiArp::Param::PANIC:
return this->arpeggiator.getPanic();
default:
return 0;
}
}
void MidiArp::keydown(int16_t pitch, uint8_t velocity)
{
MidiEvent event;
@ -40,7 +111,7 @@ void MidiArp::keyup(int16_t pitch)
this->events.push_back(event);
}
void MidiArp::process(uint16_t len)
void MidiArp::doProcess(uint16_t len)
{
arpeggiator.emptyMidiBuffer();

@ -1,31 +1,62 @@
/*
* Base AudioEffect interface
* MOD Arpeggiator port
* Ported from https://ithub.com/moddevices/mod-arpeggiator-lv2
*
* Javier Nonis (https://github.com/jnonis) - 2024
*/
#ifndef _MIDI_ARP_H
#define _MIDI_ARP_H
#include "midi_effect_base.h"
#include <vector>
#include <arm_math.h>
#include "modarpeggiator/common/commons.h"
#include "modarpeggiator/arpeggiator.hpp"
#include "modarpeggiator/common/clock.hpp"
#include "modarpeggiator/common/pattern.hpp"
#include "dexedadapter.h"
class MidiArp
class MidiArp : public MidiEffect
{
public:
static const unsigned MIDI_EFFECT_ARP = 1;
enum Param
{
BYPASS,
LATCH,
SYNC,
ARP_MODE,
DIVISION,
NOTE_LENGTH,
VELOCITY,
OCTAVE_SPREAD,
OCTAVE_MODE,
PANIC,
UNKNOWN
};
MidiArp(float32_t samplerate, CDexedAdapter* synth);
~MidiArp();
virtual ~MidiArp();
virtual unsigned getId()
{
return MIDI_EFFECT_ARP;
}
virtual void setTempo(unsigned tempo);
virtual void setParameter(unsigned param, unsigned value);
virtual unsigned getParameter(unsigned param);
void keydown(int16_t pitch, uint8_t velocity);
void keyup(int16_t pitch);
void process(uint16_t len);
protected:
bool bypass = false;
float32_t samplerate;
virtual size_t getParametersSize()
{
return MidiArp::Param::UNKNOWN;
}
virtual void doProcess(uint16_t len);
private:
static const unsigned MIDI_NOTE_OFF = 0b1000;

@ -0,0 +1,96 @@
/*
* Base MidiEffect interface
* Javier Nonis (https://github.com/jnonis) - 2024
*/
#ifndef _MIDI_EFFECT_H
#define _MIDI_EFFECT_H
#include <vector>
#include "dexedadapter.h"
class MidiEffect
{
public:
static const unsigned MIDI_EFFECT_NONE = 0;
MidiEffect(float32_t samplerate, CDexedAdapter* synth)
{
this->samplerate = samplerate;
this->synth = synth;
}
virtual ~MidiEffect()
{
}
void setBypass(bool bypass)
{
this->bypass = bypass;
}
bool getBypass()
{
return bypass;
}
virtual unsigned getId()
{
return MIDI_EFFECT_NONE;
}
virtual void setTempo(unsigned tempo)
{
}
virtual void setParameter(unsigned param, unsigned value)
{
}
virtual unsigned getParameter(unsigned param)
{
return 0;
}
void setParameters(std::vector<unsigned> params)
{
for (size_t i = 0; i < params.size(); i++)
{
this->setParameter(i, params[i]);
}
}
std::vector<unsigned> getParameters()
{
size_t len = this->getParametersSize();
std::vector<unsigned> params;
for (size_t i = 0; i < len; i++)
{
params.push_back(getParameter(i));
}
return params;
}
void process(uint16_t len)
{
if (this->bypass)
{
return;
}
doProcess(len);
}
protected:
bool bypass = false;
float32_t samplerate;
CDexedAdapter* synth;
virtual size_t getParametersSize()
{
return 0;
}
virtual void doProcess(uint16_t len)
{
}
};
#endif // _MIDI_EFFECT_H

@ -108,6 +108,7 @@ CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt,
m_pTG[i] = new CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ());
assert (m_pTG[i]);
m_MidiArpSpinLock[i] = new CSpinLock();
m_MidiArp[i] = new MidiArp(pConfig->GetSampleRate(), m_pTG[i]);
m_pTG[i]->setEngineType(pConfig->GetEngineType ());
@ -417,8 +418,13 @@ void CMiniDexed::Run (unsigned nCore)
for (unsigned i = 0; i < CConfig::TGsCore23; i++, nTG++)
{
assert (m_pTG[nTG]);
m_MidiArpSpinLock[nTG]->Acquire();
m_MidiArp[nTG]->process(m_nFramesToProcess);
m_MidiArpSpinLock[nTG]->Release();
m_pTG[nTG]->getSamples (m_OutputLevel[nTG][0],m_nFramesToProcess);
m_InsertFXSpinLock[nTG]->Acquire();
m_InsertFX[nTG]->process(m_OutputLevel[nTG][0], m_OutputLevel[nTG][0], m_OutputLevel[nTG][0], m_OutputLevel[nTG][1], m_nFramesToProcess);
m_InsertFXSpinLock[nTG]->Release();
@ -746,6 +752,7 @@ void CMiniDexed::setTempo(unsigned nValue)
for (unsigned nTG = 0; nTG < CConfig::ToneGenerators; nTG++)
{
m_InsertFX[nTG]->setTempo(m_nTempo);
m_MidiArp[nTG]->setTempo(m_nTempo);
}
// Update UI
@ -785,8 +792,16 @@ void CMiniDexed::keyup (int16_t pitch, unsigned nTG)
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_MidiArp[nTG]->keyup(pitch);
//m_pTG[nTG]->keyup (pitch);
if (m_MidiArp[nTG]->getBypass())
{
m_pTG[nTG]->keyup(pitch);
}
else
{
m_MidiArpSpinLock[nTG]->Acquire();
m_MidiArp[nTG]->keyup(pitch);
m_MidiArpSpinLock[nTG]->Release();
}
}
}
@ -798,8 +813,16 @@ void CMiniDexed::keydown (int16_t pitch, uint8_t velocity, unsigned nTG)
pitch = ApplyNoteLimits (pitch, nTG);
if (pitch >= 0)
{
m_MidiArp[nTG]->keydown(pitch, velocity);
//m_pTG[nTG]->keydown (pitch, velocity);
if (m_MidiArp[nTG]->getBypass())
{
m_pTG[nTG]->keydown (pitch, velocity);
}
else
{
m_MidiArpSpinLock[nTG]->Acquire();
m_MidiArp[nTG]->keydown(pitch, velocity);
m_MidiArpSpinLock[nTG]->Release();
}
}
}
@ -1050,6 +1073,9 @@ void CMiniDexed::SetTGParameter (TTGParameter Parameter, int nValue, unsigned nT
case TGParameterReverbSend: SetReverbSend (nValue, nTG); break;
case TGParameterInsertFXType: setInsertFXType(nValue, nTG); break;
case TGParameterMidiFXType:
// TODO
break;
default:
assert (0);
@ -1075,6 +1101,7 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
case TGParameterMIDIChannel: return m_nMIDIChannel[nTG];
case TGParameterReverbSend: return m_nReverbSend[nTG];
case TGParameterInsertFXType: return m_InsertFX[nTG]->getId();
case TGParameterMidiFXType: return m_MidiArp[nTG]->getId();
case TGParameterPitchBendRange: return m_nPitchBendRange[nTG];
case TGParameterPitchBendStep: return m_nPitchBendStep[nTG];
case TGParameterPortamentoMode: return m_nPortamentoMode[nTG];
@ -1108,6 +1135,20 @@ int CMiniDexed::GetTGParameter (TTGParameter Parameter, unsigned nTG)
}
}
void CMiniDexed::SetMidiFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType) {
assert (nTG < CConfig::ToneGenerators);
assert (m_MidiArp[nTG]->getId() == nFXType);
m_MidiArp[nTG]->setParameter(Parameter, nValue);
}
int CMiniDexed::GetMidiFXParameter (unsigned Parameter, unsigned nTG, unsigned nFXType) {
assert (nTG < CConfig::ToneGenerators);
assert (m_MidiArp[nTG]->getId() == nFXType);
return m_MidiArp[nTG]->getParameter(Parameter);
}
void CMiniDexed::SetTGFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType) {
assert (nTG < CConfig::ToneGenerators);
assert (m_InsertFX[nTG]->getId() == nFXType);
@ -1218,13 +1259,17 @@ void CMiniDexed::ProcessSound (void)
}
float32_t SampleBuffer[2][nFrames];
m_MidiArpSpinLock[0]->Acquire();
m_MidiArp[0]->process(nFrames);
m_MidiArpSpinLock[0]->Release();
m_pTG[0]->getSamples (SampleBuffer[0], nFrames);
m_InsertFXSpinLock[0]->Acquire();
m_InsertFX[0]->process(SampleBuffer[0], SampleBuffer[0], SampleBuffer[0], SampleBuffer[1], nFrames);
m_InsertFXSpinLock[0]->Release();
// Convert dual float array (left, right) to single int16 array (left/right)
float32_t tmp_float[nFrames*2];
for(uint16_t i=0; i<nFrames;i++)
@ -1276,8 +1321,13 @@ void CMiniDexed::ProcessSound (void)
for (unsigned i = 0; i < CConfig::TGsCore1; i++)
{
assert (m_pTG[i]);
m_MidiArpSpinLock[i]->Acquire();
m_MidiArp[i]->process(nFrames);
m_MidiArpSpinLock[i]->Release();
m_pTG[i]->getSamples (m_OutputLevel[i][0], nFrames);
m_InsertFXSpinLock[i]->Acquire();
m_InsertFX[i]->process(m_OutputLevel[i][0], m_OutputLevel[i][0], m_OutputLevel[i][0], m_OutputLevel[i][1], nFrames);
m_InsertFXSpinLock[i]->Release();

@ -192,6 +192,7 @@ public:
TGParameterVolume,
TGParameterPan,
TGParameterInsertFXType,
TGParameterMidiFXType,
TGParameterMasterTune,
TGParameterCutoff,
TGParameterResonance,
@ -230,6 +231,9 @@ public:
void SetTGParameter (TTGParameter Parameter, int nValue, unsigned nTG);
int GetTGParameter (TTGParameter Parameter, unsigned nTG);
void SetMidiFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType);
int GetMidiFXParameter (unsigned Parameter, unsigned nTG, unsigned nFXType);
void SetTGFXParameter (unsigned Parameter, int nValue, unsigned nTG, unsigned nFXType);
int GetTGFXParameter (unsigned Parameter, unsigned nTG, unsigned nFXType);
@ -341,6 +345,7 @@ private:
AudioEffect* m_SendFX = NULL;
float32_t m_SendFXLevel = 1.0f;
CSpinLock* m_MidiArpSpinLock[CConfig::ToneGenerators];
CSpinLock* m_InsertFXSpinLock[CConfig::ToneGenerators];
CSpinLock m_SendFXSpinLock;
CSpinLock m_ReverbSpinLock;

@ -75,6 +75,7 @@ const CUIMenu::TMenuItem CUIMenu::s_TGMenu[] =
{"Portamento", MenuHandler, s_EditPortamentoMenu},
{"Poly/Mono", EditTGParameter, 0, CMiniDexed::TGParameterMonoMode},
{"Modulation", MenuHandler, s_ModulationMenu},
{"Arp", MenuHandler, s_ModulationMenu},
{"Channel", EditTGParameter, 0, CMiniDexed::TGParameterMIDIChannel},
{"Edit Voice", MenuHandler, s_EditVoiceMenu},
{0}
@ -236,6 +237,21 @@ CUIMenu::TMenuItem CUIMenu::s_FXReverb[] =
{0}
};
CUIMenu::TMenuItem CUIMenu::s_Arp[] =
{
{"Bypass", EditTGArpParameter, 0, MidiArp::Param::BYPASS},
{"Latch", EditTGArpParameter, 0, MidiArp::Param::LATCH},
{"Sync", EditTGArpParameter, 0, MidiArp::Param::SYNC},
{"Arp Mode", EditTGArpParameter, 0, MidiArp::Param::ARP_MODE},
{"Division", EditTGArpParameter, 0, MidiArp::Param::DIVISION},
{"Note Length", EditTGArpParameter, 0, MidiArp::Param::NOTE_LENGTH},
{"Velocity", EditTGArpParameter, 0, MidiArp::Param::VELOCITY},
{"Oct Spread", EditTGArpParameter, 0, MidiArp::Param::OCTAVE_SPREAD},
{"Oct Mode", EditTGArpParameter, 0, MidiArp::Param::OCTAVE_MODE},
{"Panic", EditTGArpParameter, 0, MidiArp::Param::PANIC},
{0}
};
// inserting menu items before "OP1" affect OPShortcutHandler()
const CUIMenu::TMenuItem CUIMenu::s_EditVoiceMenu[] =
{
@ -331,6 +347,7 @@ const CUIMenu::TParameter CUIMenu::s_TGParameter[CMiniDexed::TGParameterUnknown]
{0, 127, 8, ToVolume}, // TGParameterVolume
{0, 127, 8, ToPan}, // TGParameterPan
{0, 7, 1, ToFXType}, // TGParameterInsertFXType
{0, 1, 1}, // TGParameterMidiFXType
{-99, 99, 1}, // TGParameterMasterTune
{0, 99, 1}, // TGParameterCutoff
{0, 99, 1}, // TGParameterResonance
@ -436,6 +453,21 @@ const CUIMenu::TParameter CUIMenu::s_TGFXReverbParam[AudioEffectPlateReverb::Par
{0, 99, 1}, // LEVEL
};
// must match MidiArp::Param
const CUIMenu::TParameter CUIMenu::s_ArpParam[MidiArp::Param::UNKNOWN] =
{
{0, 1, 1, ToOnOff}, // BYPASS
{0, 1, 1}, // LATCH
{0, 2, 1}, // SYNC
{0, 5, 1}, // ARP_MODE
{0, 12, 1}, // DIVISION
{0, 100, 1}, // NOTE_LENGTH
{0, 127, 1}, // VELOCITY
{1, 4, 1}, // OCTAVE_SPREAD
{0, 4, 1}, // OCTAVE_MODE
{0, 1, 1} // PANIC
};
// must match DexedVoiceParameters in Synth_Dexed
const CUIMenu::TParameter CUIMenu::s_VoiceParameter[] =
{
@ -1177,6 +1209,74 @@ void CUIMenu::EditInsertFX (CUIMenu *pUIMenu, TMenuEvent Event)
}
}
void CUIMenu::EditTGArpParameter (CUIMenu *pUIMenu, TMenuEvent Event)
{
// Get TG
unsigned nTG = pUIMenu->m_nMenuStackParameter[pUIMenu->m_nCurrentMenuDepth-2];
// Get FX type
int nFXType = pUIMenu->m_pMiniDexed->GetTGParameter(CMiniDexed::TGParameterMidiFXType, nTG);
// Get Param
unsigned nParam = pUIMenu->m_nCurrentParameter;
TParameter pParam = s_ArpParam[nParam];
const TParameter &rParam = pParam;
int nValue = pUIMenu->m_pMiniDexed->GetMidiFXParameter (nParam, nTG, nFXType);
switch (Event)
{
case MenuEventUpdate:
break;
case MenuEventPressAndStepDown:
nValue -= rParam.Increment * 9;
case MenuEventStepDown:
nValue -= rParam.Increment;
if (nValue < rParam.Minimum)
{
nValue = rParam.Minimum;
}
pUIMenu->m_pMiniDexed->SetMidiFXParameter (nParam, nValue, nTG, nFXType);
break;
case MenuEventPressAndStepUp:
nValue += rParam.Increment * 9;
case MenuEventStepUp:
nValue += rParam.Increment;
if (nValue > rParam.Maximum)
{
nValue = rParam.Maximum;
}
pUIMenu->m_pMiniDexed->SetMidiFXParameter (nParam, nValue, nTG, nFXType);
break;
default:
return;
}
string TG ("TG");
TG += to_string (nTG+1);
// Get value again after change
nValue = pUIMenu->m_pMiniDexed->GetMidiFXParameter (nParam, nTG, nFXType);
CUIMenu::TToString *pToString = rParam.ToString;
string Value;
if (pToString)
{
Value = (*pToString) (nValue);
}
else
{
Value = to_string (nValue);
}
pUIMenu->m_pUI->DisplayWrite (TG.c_str (),
pUIMenu->m_pParentMenu[pUIMenu->m_nCurrentMenuItem].Name,
Value.c_str (),
nValue > rParam.Minimum, nValue < rParam.Maximum);
}
void CUIMenu::EditTGFXParameter (CUIMenu *pUIMenu, TMenuEvent Event)
{
// Get TG

@ -99,6 +99,7 @@ private:
static void MenuHandlerInsertFX (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditInsertFX (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGFXParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static void EditTGArpParameter (CUIMenu *pUIMenu, TMenuEvent Event);
static CUIMenu::TMenuItem* getInsertFXMenuItem(unsigned type);
static void MenuHandlerSendFX (CUIMenu *pUIMenu, TMenuEvent Event);
@ -175,6 +176,8 @@ private:
static TMenuItem s_FXTalReverb3[];
static TMenuItem s_FXReverb[];
static TMenuItem s_Arp[];
static const TMenuItem s_EditVoiceMenu[];
static const TMenuItem s_OperatorMenu[];
static const TMenuItem s_SaveMenu[];
@ -194,6 +197,7 @@ private:
static const TParameter s_TGFXBigMuffParam[];
static const TParameter s_TGFXTalReverb3Param[];
static const TParameter s_TGFXReverbParam[];
static const TParameter s_ArpParam[];
static const TParameter s_VoiceParameter[];
static const TParameter s_OPParameter[];

Loading…
Cancel
Save