mirror of
https://codeberg.org/armin/trommelkiste.git
synced 2026-09-01 12:20:46 +02:00
Add VU meter, grid-quantized shuffle, and compact UI layout
This commit is contained in:
parent
9bddd6076f
commit
bd2f2dcbe6
4 changed files with 103 additions and 98 deletions
|
|
@ -65,16 +65,20 @@ void TrommelkisteProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce:
|
|||
static constexpr int shuffleDivBars[] = { 0, 4, 8, 16, 32 };
|
||||
const int divBars = shuffleDivBars[juce::jlimit(0, 4, shuffleDivIdx)];
|
||||
|
||||
// Try to get bar position from play head
|
||||
// Try to get transport info from play head
|
||||
bool hasTransport = false;
|
||||
int64_t currentBar = 0;
|
||||
double ppq = 0.0;
|
||||
double bpm = 120.0;
|
||||
if (auto* ph = getPlayHead())
|
||||
{
|
||||
auto info = ph->getPosition();
|
||||
if (info.hasValue() && info->getPpqPosition().hasValue())
|
||||
{
|
||||
hasTransport = true;
|
||||
double ppq = *info->getPpqPosition();
|
||||
ppq = *info->getPpqPosition();
|
||||
if (info->getBpm().hasValue())
|
||||
bpm = *info->getBpm();
|
||||
double beatsPerBar = 4.0;
|
||||
if (info->getTimeSignature().hasValue())
|
||||
beatsPerBar = (double)(*info->getTimeSignature()).numerator;
|
||||
|
|
@ -84,8 +88,14 @@ void TrommelkisteProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce:
|
|||
|
||||
const bool barAllowed = (divBars == 0) || !hasTransport || (currentBar % divBars == 0);
|
||||
|
||||
// 16th note grid quantization (DAW shuffle is reflected in PPQ)
|
||||
const double ppqPerSample = bpm / (60.0 * currentSampleRate);
|
||||
const double sixteenthPpq = 0.25;
|
||||
auto nextGridPpq = [&](double fromPpq) -> double {
|
||||
return std::ceil((fromPpq + 0.0001) / sixteenthPpq) * sixteenthPpq;
|
||||
};
|
||||
|
||||
// Process pending shuffle events
|
||||
const int baseDelay = (int)(currentSampleRate * 0.05 * globalGroove); // up to 50ms at max groove
|
||||
for (int i = numPendingShuffle - 1; i >= 0; --i)
|
||||
{
|
||||
pendingShuffle[i].delaySamples -= numSamples;
|
||||
|
|
@ -96,18 +106,18 @@ void TrommelkisteProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce:
|
|||
}
|
||||
}
|
||||
|
||||
// Independent shuffle: generate events even without MIDI input
|
||||
// Independent shuffle: generate events even without MIDI input, on the same track
|
||||
std::uniform_real_distribution<float> dist(0.0f, 1.0f);
|
||||
if (globalShuffleAmt > 0.0f && barAllowed)
|
||||
if (globalShuffleAmt > 0.0f && barAllowed && lastTriggeredTrack >= 0)
|
||||
{
|
||||
std::uniform_int_distribution<int> trackDist(0, NUM_TRACKS - 1);
|
||||
float prob = globalShuffleAmt * 0.15f; // base probability per block
|
||||
float prob = globalShuffleAmt * 0.15f;
|
||||
if (dist(rng) < prob && numPendingShuffle < MAX_PENDING_SHUFFLE)
|
||||
{
|
||||
int target = trackDist(rng);
|
||||
float vel = (0.3f + dist(rng) * 0.5f) * globalShuffleAmt;
|
||||
int delay = (int)(baseDelay * (0.5f + dist(rng) * 0.5f));
|
||||
pendingShuffle[numPendingShuffle++] = { target, vel, delay };
|
||||
int stepsAhead = std::uniform_int_distribution<int>(1, 4)(rng);
|
||||
double gridPpq = nextGridPpq(ppq) + stepsAhead * sixteenthPpq;
|
||||
int delay = juce::jmax(1, (int)((gridPpq - ppq) / ppqPerSample));
|
||||
pendingShuffle[numPendingShuffle++] = { lastTriggeredTrack, vel, delay };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,6 +130,7 @@ void TrommelkisteProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce:
|
|||
if (t >= 0 && t < NUM_TRACKS)
|
||||
{
|
||||
handleNoteOn(t, msg.getFloatVelocity());
|
||||
lastTriggeredTrack = t;
|
||||
|
||||
const float shuffleAmt = apvts.getRawParameterValue(
|
||||
"t" + juce::String(t) + "_shuffle")->load();
|
||||
|
|
@ -127,26 +138,17 @@ void TrommelkisteProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce:
|
|||
|
||||
if (effShuffle > 0.0f && barAllowed)
|
||||
{
|
||||
int maxEvents = juce::jmax(1, (int)(effShuffle * 3.0f + 0.5f));
|
||||
int numEvents = std::uniform_int_distribution<int>(0, maxEvents)(rng);
|
||||
int numEvents = juce::jmax(1, (int)(effShuffle * 3.0f + 0.5f));
|
||||
numEvents = std::uniform_int_distribution<int>(0, numEvents)(rng);
|
||||
|
||||
for (int e = 0; e < numEvents; ++e)
|
||||
{
|
||||
bool sameTrack = dist(rng) < 0.35f;
|
||||
int target = t;
|
||||
if (!sameTrack)
|
||||
{
|
||||
target = std::uniform_int_distribution<int>(0, NUM_TRACKS - 1)(rng);
|
||||
if (target == t)
|
||||
target = (t + 1 + std::uniform_int_distribution<int>(0, NUM_TRACKS - 2)(rng)) % NUM_TRACKS;
|
||||
}
|
||||
|
||||
float vel = msg.getFloatVelocity() * (0.3f + dist(rng) * 0.7f) * effShuffle;
|
||||
int stepsAhead = std::uniform_int_distribution<int>(1, 4)(rng);
|
||||
double gridPpq = nextGridPpq(ppq) + stepsAhead * sixteenthPpq;
|
||||
int delay = juce::jmax(1, (int)((gridPpq - ppq) / ppqPerSample));
|
||||
if (numPendingShuffle < MAX_PENDING_SHUFFLE)
|
||||
{
|
||||
int delay = (int)(baseDelay * (0.5f + dist(rng) * 0.5f));
|
||||
pendingShuffle[numPendingShuffle++] = { target, vel, delay };
|
||||
}
|
||||
pendingShuffle[numPendingShuffle++] = { t, vel, delay };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -822,7 +824,7 @@ juce::AudioProcessorValueTreeState::ParameterLayout TrommelkisteProcessor::creat
|
|||
|
||||
layout.add(std::make_unique<AudioParameterFloat>(
|
||||
"global_shuffleAmount", "Shuffle Amt",
|
||||
NormalisableRange<float>(0.0f, 1.0f, 0.01f), 1.0f));
|
||||
NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.0f));
|
||||
|
||||
layout.add(std::make_unique<AudioParameterFloat>(
|
||||
"global_groove", "Groove",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue