From b272c3f02756acfe12f4f94a8ad40a285ba1f29b Mon Sep 17 00:00:00 2001 From: soveda <161259864+soveda@users.noreply.github.com> Date: Thu, 25 Jun 2026 21:58:46 +0100 Subject: [PATCH] applied MIDI changes and adapted info.yaml --- releases/74_Wild_Pebble/README.md | 18 +- releases/74_Wild_Pebble/UF2/WildPebble.uf2 | Bin 66560 -> 67584 bytes releases/74_Wild_Pebble/WildPebble.cpp | 196 +++++++++++++++++---- releases/74_Wild_Pebble/info.yaml | 193 ++++++++++---------- 4 files changed, 278 insertions(+), 129 deletions(-) diff --git a/releases/74_Wild_Pebble/README.md b/releases/74_Wild_Pebble/README.md index 8a80e77e3..9355970f1 100644 --- a/releases/74_Wild_Pebble/README.md +++ b/releases/74_Wild_Pebble/README.md @@ -23,9 +23,9 @@ Wild Pebble listens for USB MIDI realtime clock messages. * MIDI Continue `0xFB` resumes MIDI clock following without resetting the step. * MIDI Stop `0xFC` stops MIDI clock following and releases any active MIDI note. -MIDI clock is 24 PPQN. Wild Pebble advances one sequencer step every 6 MIDI clock ticks, so each step behaves as a 16th note. +MIDI clock is 24 PPQN. Wild Pebble advances one sequencer step every 12 MIDI clock ticks, so incoming MIDI clock drives the sequencer at an 8th-note step feel. -When MIDI clock is active it overrides the internal clock, in the same spirit as the pulse clock input. If no MIDI or pulse clock is active, the Main knob controls the internal clock speed. +When MIDI clock is active it overrides the internal clock. If Pulse Input 1 is active and running faster than MIDI, the pulse clock takes priority. If no MIDI or pulse clock is active, the Main knob controls the internal clock speed. ## MIDI Note Output @@ -33,10 +33,12 @@ The generated melody is sent over USB MIDI as note events. * MIDI channel: 1 * Note source: the current generated `currentMIDINote` -* Gate source: Pulse Output 1 -* Velocity: derived from the current internal energy value +* Trigger source: Pulse Output 1 +* Velocity: shaped by internal energy and tension +* Note length: shaped by the active clock period, energy, and tension +* Retriggers: become more likely as energy and tension rise -Each Pulse Output 1 gate produces a MIDI note on, followed by a note off when the gate ends. The analogue CV and pulse outputs continue to work as in the original card. +This keeps the MIDI line feeling connected to the card's internal motion instead of acting like a rigid copy of the raw pulse width. The analogue CV and pulse outputs continue to work as in the original card. --- @@ -135,15 +137,15 @@ CV Input 2 modulates mutation amount. ### Up -Stable melodic motion, restrained mutation, slower harmonic movement, and tighter rhythms. +Steady mode. Stable melodic motion, restrained mutation, slower harmonic movement, and tighter rhythms. ### Middle -Balanced mutation, moderate swing, evolving melodic variation, and gradual harmonic drift. +Drift mode. Balanced mutation, moderate swing, evolving melodic variation, and gradual harmonic drift. ### Down -Aggressive mutation, strongest swing, wider melodic jumps, more active scale changes, and denser companion rhythms. +Surge mode. Aggressive mutation, strongest swing, wider melodic jumps, more active scale changes, and denser companion rhythms. --- diff --git a/releases/74_Wild_Pebble/UF2/WildPebble.uf2 b/releases/74_Wild_Pebble/UF2/WildPebble.uf2 index c38aad4c88d91b8356996b684537937d6c392d53..341404802c2f7cd34a260547db8eee7c301ebc33 100644 GIT binary patch delta 12902 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

{ZI*nxAjut}Ho6wk^1U`lJk#kNMlHX!=f|A( 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 delta 12078 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 diff --git a/releases/74_Wild_Pebble/WildPebble.cpp b/releases/74_Wild_Pebble/WildPebble.cpp index 82f046058..36f589277 100644 --- a/releases/74_Wild_Pebble/WildPebble.cpp +++ b/releases/74_Wild_Pebble/WildPebble.cpp @@ -42,7 +42,7 @@ static constexpr uint8_t kMidiStop = 0xFC; static constexpr uint8_t kMidiNoteOff = 0x80; static constexpr uint8_t kMidiNoteOn = 0x90; static constexpr uint8_t kMidiChannel = 0; -static constexpr uint8_t kMidiClocksPerStep = 6; +static constexpr uint8_t kMidiClocksPerStep = 12; volatile uint8_t gMidiClockTicksPending = 0; volatile uint8_t gMidiStartPending = 0; @@ -123,11 +123,19 @@ class WildPebble : public ComputerCard uint32_t internalClockPeriod = 4000; bool externalClockActive = false; - uint32_t externalClockTimeout = 0; + uint32_t pulseClockTimeout = 0; + uint32_t midiClockTimeout = 0; + uint32_t lastPulseClockSample = 0; + uint32_t lastMidiStepSample = 0; + uint32_t pulseClockPeriod = 0xFFFFFFFF; + uint32_t midiClockPeriod = 0xFFFFFFFF; bool midiClockRunning = false; uint8_t midiClockDivider = 0; bool midiNoteGateActive = false; uint8_t midiLastNote = 48; + uint32_t midiNoteOffSample = 0; + uint32_t midiNoteOnSample = 0; + uint32_t selectedClockPeriod = 4000; int32_t tension = 64; bool tensionRising = true; @@ -515,6 +523,107 @@ class WildPebble : public ComputerCard } } + uint32_t ActiveStepPeriod() const + { + if(selectedClockPeriod > 0) + { + return selectedClockPeriod; + } + + return internalClockPeriod; + } + + uint8_t MidiVelocity() const + { + int32_t velocity = 28 + (currentEnergy >> 1) + (tension >> 2); + + if(velocity > 127) + { + velocity = 127; + } + + if(velocity < 24) + { + velocity = 24; + } + + return (uint8_t)velocity; + } + + uint32_t MidiNoteLength() const + { + uint32_t stepPeriod = ActiveStepPeriod(); + uint32_t shape = (uint32_t)(80 + (currentEnergy >> 1) + (tension >> 2)); + + if(shape > 240) + { + shape = 240; + } + + uint32_t noteLength = (stepPeriod * shape) >> 8; + + if(noteLength < 120) + { + noteLength = 120; + } + + return noteLength; + } + + bool ShouldRetriggerMidiNote() + { + if(!midiNoteGateActive) + { + return true; + } + + uint32_t minGap = ActiveStepPeriod() >> 3; + + if(minGap < 120) + { + minGap = 120; + } + + if((sampleCounter - midiNoteOnSample) < minGap) + { + return false; + } + + uint32_t retriggerChance = (uint32_t)((currentEnergy >> 1) + (tension >> 2)); + + if(retriggerChance > 220) + { + retriggerChance = 220; + } + + return (Random() & 255) < retriggerChance; + } + + void TriggerMidiNote() + { + if(!ShouldRetriggerMidiNote()) + { + return; + } + + if(midiNoteGateActive) + { + QueueMidiMessage(kMidiNoteOff | kMidiChannel, + midiLastNote, + 0); + } + + midiLastNote = (uint8_t)currentMIDINote; + + QueueMidiMessage(kMidiNoteOn | kMidiChannel, + midiLastNote, + MidiVelocity()); + + midiNoteGateActive = true; + midiNoteOnSample = sampleCounter; + midiNoteOffSample = sampleCounter + MidiNoteLength(); + } + bool ConsumeMidiClock() { bool clockEvent = false; @@ -526,16 +635,13 @@ class WildPebble : public ComputerCard midiClockDivider = 0; currentStep = -1; clockCounter = 0; - externalClockActive = true; - externalClockTimeout = 48000; + lastMidiStepSample = sampleCounter; } if(gMidiContinuePending > 0) { gMidiContinuePending--; midiClockRunning = true; - externalClockActive = true; - externalClockTimeout = 48000; } if(gMidiStopPending > 0) @@ -543,8 +649,8 @@ class WildPebble : public ComputerCard gMidiStopPending--; midiClockRunning = false; midiClockDivider = 0; - externalClockActive = false; - externalClockTimeout = 0; + midiClockTimeout = 0; + midiClockPeriod = 0xFFFFFFFF; if(midiNoteGateActive) { @@ -564,14 +670,15 @@ class WildPebble : public ComputerCard continue; } - externalClockActive = true; - externalClockTimeout = 48000; + midiClockTimeout = 48000; midiClockDivider++; if(midiClockDivider >= kMidiClocksPerStep) { midiClockDivider = 0; + midiClockPeriod = sampleCounter - lastMidiStepSample; + lastMidiStepSample = sampleCounter; clockEvent = true; break; } @@ -582,20 +689,8 @@ class WildPebble : public ComputerCard void UpdateMidiNoteOutput() { - if(pulse1 && !midiNoteGateActive) - { - midiLastNote = (uint8_t)currentMIDINote; - - uint8_t velocity = - (uint8_t)(40 + ((currentEnergy * 87) >> 8)); - - QueueMidiMessage(kMidiNoteOn | kMidiChannel, - midiLastNote, - velocity); - - midiNoteGateActive = true; - } - else if(!pulse1 && midiNoteGateActive) + if(midiNoteGateActive && + ((int32_t)(sampleCounter - midiNoteOffSample) >= 0)) { QueueMidiMessage(kMidiNoteOff | kMidiChannel, midiLastNote, @@ -633,24 +728,58 @@ class WildPebble : public ComputerCard bool freeze = PulseIn2(); - bool clockEvent = ConsumeMidiClock(); + bool midiClockEvent = ConsumeMidiClock(); + bool pulseClockEvent = false; if(PulseIn1RisingEdge()) { - externalClockActive = true; - externalClockTimeout = 48000; - clockEvent = true; + pulseClockPeriod = sampleCounter - lastPulseClockSample; + lastPulseClockSample = sampleCounter; + pulseClockTimeout = 48000; + pulseClockEvent = true; + } + + if(pulseClockTimeout > 0) + { + pulseClockTimeout--; + } + + if(midiClockTimeout > 0) + { + midiClockTimeout--; } - if(externalClockTimeout > 0) + bool pulseClockActive = pulseClockTimeout > 0; + bool midiClockActive = + midiClockRunning && + (midiClockTimeout > 0); + + bool preferPulseClock = + pulseClockActive && + (!midiClockActive || + (pulseClockPeriod < midiClockPeriod)); + + bool clockEvent = false; + + if(preferPulseClock) + { + clockEvent = pulseClockEvent; + selectedClockPeriod = pulseClockPeriod; + } + else if(midiClockActive) { - externalClockTimeout--; + clockEvent = midiClockEvent; + selectedClockPeriod = midiClockPeriod; } else { - externalClockActive = false; + selectedClockPeriod = internalClockPeriod; } + externalClockActive = + pulseClockActive || + midiClockActive; + if(!externalClockActive) { swingOffset = 0; @@ -687,6 +816,11 @@ class WildPebble : public ComputerCard if(clockEvent) { AdvanceStep(density, mode); + + if(pulse1) + { + TriggerMidiNote(); + } // Update S+H only when Pulse2 fires diff --git a/releases/74_Wild_Pebble/info.yaml b/releases/74_Wild_Pebble/info.yaml index fe11af4a3..40ae41c4d 100644 --- a/releases/74_Wild_Pebble/info.yaml +++ b/releases/74_Wild_Pebble/info.yaml @@ -1,105 +1,118 @@ -draft: true -Name: Wild Pebble -Description: MIDI-clockable generative rhythm and melody organism inspired by Pet Rock -Language: C++ -Creator: Adrian Vos with Vibecode support -Version: 1.0 -Status: Beta. USB MIDI clock input and sequencer note output tested -License: GPL-3.0 - -manual: | - Generative rhythm/melody organism with coupled trigger streams, quantized melody CV, - internal kick/snare voices, and evolving energy/tension modulation. Main controls - internal tempo when no external clock is present, X controls rhythmic density, and Y - controls mutation amount. Switch up/middle/down sets increasingly active mutation and - swing behavior. Pulse In 1 is external clock and Pulse In 2 freezes mutation while high. - +id: 74_Wild_Pebble +title: Wild Pebble +draft: false +release: 74 / 1.0 +summary: MIDI-clockable generative rhythm and melody organism inspired by Pet Rock +description: MIDI-clockable generative rhythm and melody organism inspired by Pet Rock panel: + controls: + main: + label: |- + Internal + Tempo + description: Internal clock speed with variable swing profile according to switch position when internally clocked + source: info.yaml + x: + label: Density + description: Trigger probability density control (modulated by CVIn1) + source: info.yaml + 'y': + label: Mutation + description: Mutation intensity control (modulated by CVIn2) + source: info.yaml + z: + label: |- + Mode + Select + description: Selects the overall behavior mode, shifting the card from restrained to more animated swing and mutation + source: info.yaml inputs: - - id: PulseIn1 - name: External Clock + pulse_1: + label: Ext Clock description: External clock input; when active it overrides internal tempo clock - - id: PulseIn2 - name: Freeze Gate + source: info.yaml + pulse_2: + label: Freeze Gate description: While held high, mutation updates are disabled and structure is preserved - - id: CVIn1 - name: Density Modulation + source: info.yaml + cv_1: + label: Density Mod description: Modulates X density control - - id: CVIn2 - name: Mutation Modulation + source: info.yaml + cv_2: + label: Mutation Mod description: Modulates Y mutation control + source: info.yaml outputs: - - id: PulseOut1 - name: Primary Trigger Stream + pulse_out_1: + label: |- + Primary Trig + Stream description: Main rhythm trigger stream used for melodic progression and kick events - - id: PulseOut2 - name: Companion Trigger Stream + source: info.yaml + pulse_out_2: + label: |- + Companion + Trig Stream description: Derived companion trigger stream used for snare/percussion events - - id: CVOut1 - name: Quantized Melody CV + source: info.yaml + cv_out_1: + label: |- + Quant Melody + CV description: Quantized pitch CV from evolving scale-constrained sequence - - id: CVOut2 - name: Energy/Tension CV + source: info.yaml + cv_out_2: + label: |- + Energy / + Tension CV description: Smoothed evolving modulation output from internal energy and tension state - - id: AudioOut1 - name: Kick Voice + source: info.yaml + audio_out_l: + label: Kick Voice description: Internal kick/percussion synthesis output driven by primary trigger stream - - id: AudioOut2 - name: Snare Voice + source: info.yaml + audio_out_r: + label: Snare Voice description: Internal snare/percussion synthesis output driven by companion trigger stream + source: info.yaml +switch_modes: + up: 'Z up: Steady mode. Restrained mutation, tighter rhythms, and the calmest phrasing feel.' + middle: 'Z middle: Drift mode. Balanced mutation, moderate swing, and gradual harmonic movement.' + down: 'Z down: Surge mode. Strongest swing, wider melodic leaps, and the most animated behavior.' +leds: + - >- + Z any: Primary Trigger; Density Amount; Mutation Amount; Energy State; Clock Source; + Tension State. +source: + - releases/74_Wild_Pebble/info.yaml + - releases/74_Wild_Pebble/README.md +slug: 74-wild-pebble +url: /workshopsystem/program-cards/74-wild-pebble/ +tags: [] +source_file: releases/74_Wild_Pebble/info.yaml +source_url: https://github.com/TomWhitwell/Workshop_Computer/tree/main/releases/74_Wild_Pebble +readme_url: >- + https://github.com/TomWhitwell/Workshop_Computer/blob/main/releases/74_Wild_Pebble/README.md +download_url: >- + https://raw.githubusercontent.com/TomWhitwell/Workshop_Computer/main/releases/74_Wild_Pebble/UF2/WildPebble.uf2 +metadata: + creator: Adrian Vos with Vibecode support + language: C++ + version: 1 + status: Beta. USB MIDI clock input and sequencer note output tested + license: GPL-3.0 +documentation: + intro: > + Generative rhythm/melody organism with coupled trigger streams, quantized melody CV, + + internal kick/snare voices, evolving energy/tension modulation, and USB MIDI clock/note + + behavior. Main controls internal tempo when no external clock is present, X controls + + rhythmic density, and Y controls mutation amount. The switch selects Steady, Drift, or -controls: - knobs: - - when: { z: up } - main: - name: Internal Tempo - description: Internal clock speed when no external clock is present - x: - name: Density - description: Trigger probability density control (modulated by CVIn1) - y: - name: Mutation - description: Mutation intensity control (modulated by CVIn2) - - when: { z: middle } - main: - name: Internal Tempo - description: Internal clock speed with moderate swing profile when internally clocked - x: - name: Density - description: Trigger probability density control (modulated by CVIn1) - y: - name: Mutation - description: Mutation intensity control (modulated by CVIn2) - - when: { z: down } - main: - name: Internal Tempo - description: Internal clock speed with strongest swing profile when internally clocked - x: - name: Density - description: Trigger probability density control with more active companion-rhythm behavior - y: - name: Mutation - description: Aggressive mutation behavior and faster harmonic movement + Surge behavior, moving from restrained patterns to more animated swing, mutation, and - leds: - - when: { z: any } - display: list - items: - - id: LED0 - name: Primary Trigger - description: Flashes when PulseOut1 fires - - id: LED1 - name: Density Amount - description: Brightness follows density control value - - id: LED2 - name: Mutation Amount - description: Brightness follows mutation control value - - id: LED3 - name: Energy State - description: Displays smoothed internal energy/modulation state - - id: LED4 - name: Clock Source - description: Lit when external clock is active - - id: LED5 - name: Tension State - description: Displays evolving internal tension state + melodic activity. Pulse In 1 is external clock and Pulse In 2 freezes mutation while + high.