[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 midilab\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# uClock - BPM Clock Generator Library\n\nA professional-grade BPM clock generator library for Arduino and PlatformIO, designed for musicians, artists, and engineers creating sequencers, sync boxes, and real-time musical devices. It is built to be multi-architecture, portable, and easy to use within the open-source ecosystem.\n\n## Overview\n\n**uClock** delivers precise, hardware-interrupt-driven clock timing for music and media applications. Whether you're building a MIDI sequencer, modular synthesizer clock, or synchronizing multiple devices, uClock provides the rock-solid timing foundation your project needs.\n\nThe library leverages hardware timer interrupts to ensure accurate BPM generation and synchronization, making it suitable for professional music production, live performance, and creative installations.\n\nThe absence of real-time features necessary for creating professional-level embedded devices for music and video on open-source community-based platforms like Arduino led to the development of uClock. By leveraging timer hardware interrupts, the library can schedule and manage real-time processing with safe shared resource access through its API.\n\n## Supported Platforms\n\n- **AVR**: ATmega168/328, ATmega16u4/32u4, ATmega2560\n- **ARM**: Teensy (all versions), STM32XX, Seeed Studio XIAO M0\n- **ESP32**: All ESP32 family boards\n- **RP2040**: Raspberry Pi Pico and compatible boards\n\n## Why uClock?\n\nOpen-source platforms like Arduino and PlatformIO traditionally lack the real-time capabilities required for professional music and video applications. uClock bridges this gap by providing:\n\n- **Precise timing** through hardware interrupts\n- **Flexible clock resolutions** from 1 to 960 PPQN\n- **External sync support** for external sync configurations\n- **Shuffle and groove** capabilities for humanized timing\n- **Multi-track sequencing** with independent shuffle per track\n- **Multiple sync outputs** for different device standards\n\n## Installation\n\n### PlatformIO\n1. Open platformio.ini, a project configuration file located in the root of PlatformIO project.\n2. Add the following line to the lib_deps option of [env:] section: midilab/uClock@^2.3.0\n```ini\n[env:...]\nlib_deps =\n    midilab/uClock@^2.3.0\n```\n3. Build a project, PlatformIO will automatically install dependencies.\n\n### Arduino IDE\n1. Open your Arduino IDE\n2. Select the Library Manager Tab on left side\n3. Type \"uclock\" at the search box\n4. Click Install for latest version\n\n## Core Concepts\n\n### Clock Resolutions (PPQN)\n\nPPQN (Pulses Per Quarter Note) determines the timing resolution of your clock:\n\n- **PPQN_1, 2, 4, 8, 12**: Modular synthesis sync standards\n- **PPQN_24**: Standard MIDI sync (24 pulses per beat)\n- **PPQN_48**: Common in vintage drum machines\n- **PPQN_96**: High-resolution internal clock (default)\n- **PPQN_384, 480, 960**: Ultra-high resolution for modern sequencing and DAWs\n\n### Callback Architecture\n\nuClock operates through a callback system that triggers your code at precise intervals:\n\n| Callback | Purpose | Use Case |\n|----------|---------|----------|\n| `setOnOutputPPQN()` | Main clock pulse | Drive sequencers, process MIDI |\n| `setOnStep()` | 16th note intervals | Step sequencers, drum patterns |\n| `setOnSync()` | Custom sync outputs | Multiple device sync, modular CV |\n| `setOnClockStart()` | Clock start event | Initialize sequences, send MIDI start |\n| `setOnClockStop()` | Clock stop event | Reset states, send MIDI stop |\n| `setOnClockPause()` | Clock pause event | Pause handling |\n| `setOnClockContinue()` | Clock continue event | Resume from pause |\n\n## Quick Start\n\n### Basic 96 PPQN Clock\n\n```cpp\n#include <uClock.h>\n\nvoid onPPQNCallback(uint32_t tick) {\n    // Called at each clock pulse (default 96 PPQN)\n    // Drive your sequencer logic here\n}\n\nvoid setup() {\n    // set main clock rate for output(sequencer resolution)\n    uClock.setOutputPPQN(uClock.PPQN_96);\n    // Configure callbacks\n    uClock.setOnOutputPPQN(onPPQNCallback);\n\n    // Set tempo\n    uClock.setTempo(120.0);\n\n    // Initialize and start\n    uClock.init();\n    uClock.start();\n}\n\nvoid loop() {\n    // Your main code here\n}\n```\n\n### Basic MIDI Clock box with External Sync\nSend clock message and sync to external midi clock.\n\n```cpp\n#include <uClock.h>\n\n// MIDI clock, start and stop byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n\n// The callback function called by Clock each Pulse of 24PPQN clock resolution.\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  Serial.write(MIDI_CLOCK);\n}\n\n// The callback function called when clock starts by using Clock.start() method.\nvoid onClockStart() {\n  Serial.write(MIDI_START);\n}\n\n// The callback function called when clock stops by using Clock.stop() method.\nvoid onClockStop() {\n  Serial.write(MIDI_STOP);\n}\n\nvoid setup() {\n\n  // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication:\n  Serial.begin(31250);\n\n  // set main clock rate for output(sequencer resolution)\n  uClock.setOutputPPQN(uClock.PPQN_96);\n  // set main clock rate for input(expected sync signal rate)\n  uClock.setInputPPQN(uClock.PPQN_24);\n  \n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  \n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // set external clock mode\n  uClock.setClockMode(uClock.EXTERNAL_CLOCK);\n\n  // Set the clock BPM to 126 BPM\n  //uClock.setTempo(126);\n  //uClock.start();\n}\n\nvoid loop() {\n  // call clockMe() each time you receive an external clock pulse\n  // in this example we set inputPPQN to 24 PPQN\n  // wich expects a signal clock rate comming from MIDI device\n  // PS: Idealy you should do midi sync with another interruption schema\n  // the more code on loop() the less accuracy the MIDI_CLOCK signal reads\n  if (Serial.available() > 0) {\n    uint8_t midi_byte = Serial.read();\n    \n    switch (midi_byte) {\n        case MIDI_CLOCK:\n            uClock.clockMe();\n            break;\n    \n        case MIDI_START:\n            uClock.start(); \n            break;\n    \n        case MIDI_STOP:\n            uClock.stop();\n            break;\n    }\n  }\n}\n```\n\n## Advanced Features\n\n### Multiple Sync Outputs\n\nGenerate different clock resolutions simultaneously for various devices:\n\n```cpp\n#include <uClock.h>\n\n#define SYNC_OUT_PIN 8\n#define MIDI_CLOCK_BYTE 0xF8\n\nvoid onSync1(uint32_t tick) {\n    // Send modular sync (1 pulse per quarter note)\n    triggerModularPulse();\n}\n\nvoid onSync2(uint32_t tick) {\n    // Send Pocket Operators sync (2 pulse per quarter note)\n    triggerPocketOperatorsPulse();\n}\n\nvoid onSync24(uint32_t tick) {\n    // Send MIDI clock (24 PPQN)\n    Serial.write(MIDI_CLOCK_BYTE);\n}\n\nvoid onSync48(uint32_t tick) {\n    // Send 48 PPQN for vintage gear like Korg DIN Sync 48\n    digitalWrite(SYNC_OUT_PIN, !digitalRead(SYNC_OUT_PIN));\n}\n\nvoid setup() {    \n    // set main clock rate for output(sequencer resolution)\n    uClock.setOutputPPQN(uClock.PPQN_96);\n    // set sync callbacks\n    uClock.setOnSync(uClock.PPQN_1, onSync1);\n    uClock.setOnSync(uClock.PPQN_2, onSync2);\n    uClock.setOnSync(uClock.PPQN_24, onSync24);\n    uClock.setOnSync(uClock.PPQN_48, onSync48);\n    // do only init after all setup is done\n    uClock.init();\n    \n    // Set the clock BPM to 126 BPM\n    uClock.setTempo(126);\n    uClock.start();\n}\n\nvoid loop() {\n    // Your main code here\n}\n```\n\n**Available Sync Resolutions**: All possible Clock Resolutions (PPQN) where setOnSync(resolution) <= setOutputPPQN(resolution)\n\n### Step Sequencer Extension\n\nuClock includes a built-in extension specifically designed for creating step sequencers for synthesizers and drum machines. The extension provides multi-track support with independent per-track control, making it ideal for building complete rhythm machines and melodic sequencers.\n\n#### Features\n\n**Current Features**:\n- ✅ **16th note orientation**: Natural step sequencer workflow\n- ✅ **Multi-track support**: Independent sequences with individual callbacks\n- ✅ **Per-track shuffle**: Each track can have its own groove templatexs\n\n**Roadmap**:\n- 🔄 **Per-track shift**: Offset patterns in time (coming soon)\n- 🔄 **Per-track direction**: Forward, reverse, ping-pong playback (coming soon)\n\n```cpp\n#include <uClock.h>\n\n#define MAX_STEPS 16\n//#define TRACK_NUMBER 8\n\n// a pattern example for drums mainly\nuint8_t pattern[MAX_STEPS] = {1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0};\n\n// Called every 16th note\n// not dependent on internal or external clock resolution\n// single track callback(doesn't mean you can't code a multirack sequencer here)\nvoid onStepCallback(uint32_t step) {\n    if (pattern[step % MAX_STEPS])\n        playNote();\n}\n\n// the multirack callback \n//void onStepCallback(uint32_t step, uint8_t track) {\n//}\n\nvoid setup() {\n    // Configure callbacks\n    uClock.setOnStep(onStepCallback);\n    // with multitrack support?\n    //uClock.setOnStep(onStepCallback, TRACK_NUMBER);\n    \n    uClock.init();\n    \n    // Set the clock BPM to 126 BPM\n    uClock.setTempo(126);\n    uClock.start();\n}\n\nvoid loop() {\n    // Your main code here\n}\n```\n\n### Shuffle and Groove\n\nAdd humanization and swing to your sequences with shuffle support. Re-create timeless groove signatures of legends like MPC60 or TR-909, or experiment with your own custom groove templates.\n\n#### How Shuffle Works\n\n![Ableton Shuffle Example](https://raw.githubusercontent.com/midilab/uclock/main/doc/shuflle-example.gif)\n\nShuffle operates by shifting individual steps earlier or later in time, along with adjusting note lengths to maintain musical coherence. As shown in the Ableton example above:\n\n- **Positive shuffle values**: Delay the note trigger and shorten note length (to avoid overlapping with the next note)\n- **Negative shuffle values**: Trigger the note earlier and extend note length (filling the time gap)\n- **Zero values**: Play straight with no shuffle effect\n\nThis is the fundamental principle behind groove templating in step sequencers. Each 16th note step can be individually time-shifted to create swing, shuffle, or completely custom rhythmic feels.\n\n#### Technical Specifications\n\n**Shuffle templates are PPQN-dependent**: The range of shuffle values depends on your output resolution:\n\n```cpp\n// Shuffle range calculation - in ticks\nmin_shuffle = -(output_ppqn/4) - 1\nmax_shuffle = +(output_ppqn/4) - 1\n\n// Example with PPQN_96 (default)\n// Range: -23 to +23 ticks per step\n```\n\n**Template Size Configuration**: Adjust in `uClock.h` if needed:\n\n```cpp\n#define MAX_SHUFFLE_TEMPLATE_SIZE   16\n```\n\nModify this value to support longer patterns (memory permitting) or reduce it to save memory if you only need simpler grooves.\n\nMPC60 Groove implmentation example based on [Roger Linn Groove Magic Interview](https://www.attackmagazine.com/features/interview/roger-linn-swing-groove-magic-mpc-timing/)\n\n```cpp\n#include <uClock.h>\n\n#define TRACKS_SIZE 8\n\n// The shuffle effect is applied transparently for shuffled steps\nvoid onStepCallback(uint32_t step, uint8_t track) {\n    // get the length diff to apply to current shuffled step length\n    // if the step is not shuffled, then shuffle_len == 0\n    int8_t shuffle_len = uClock.getShuffleLength(track);\n    // sequencer procesing...\n    // ...\n    uint8_t step_len = getNoteLength(step, track);\n    step_len += shuffle_len;\n    // use step_len as the new length of note for this step\n    // ...\n}\n\n// no multirack support gives you a global shuffle flavor\n//void onStepCallback(uint32_t step) {\n//}\n\nvoid setup() {\n    // MPC60 groove signature (Same used for TR909)\n    // Based on Roger Linn Groove Magic Interview\n    // Internal clock: 96 PPQN\n    // Each step is 24 ticks long\n    uint8_t current_shuffle = 0;\n    int8_t shuffle_50[2] = {0, 0};\n    int8_t shuffle_54[2] = {0, 2};\n    int8_t shuffle_58[2] = {0, 4};\n    int8_t shuffle_62[2] = {0, 6};\n    int8_t shuffle_66[2] = {0, 8};\n    int8_t shuffle_71[2] = {0, 10};\n    int8_t shuffle_75[2] = {0, 12};\n    String shuffle_name[7] = {\"50%\", \"54%\", \"58%\", \"62%\", \"66%\", \"71%\", \"75%\"};\n    int8_t* shuffle_templates[7] = {shuffle_50, shuffle_54, shuffle_58, shuffle_62, shuffle_66, shuffle_71, shuffle_75};\n    \n    // set main clock rate for output(sequencer resolution) and input(expected sync signal)\n    uClock.setOutputPPQN(uClock.PPQN_96);\n    // the second argument of setOnStep is used to request multitrack support\n    // multitrack individual shuffle support for this example\n    uClock.setOnStep(onStepCallback, TRACKS_SIZE);\n    // global shuffle? no need to multitrack handle of step sequencer extension\n    // set the callback without second param\n    //uClock.setOnStep(onStepCallback);\n    \n    uClock.init();\n    \n    // set a template for shuffle for specific tracks\n    uClock.setShuffleTemplate(shuffle_templates[current_shuffle], 0);\n    uClock.setShuffleTemplate(shuffle_templates[current_shuffle], 1);\n    uClock.setShuffleTemplate(shuffle_templates[current_shuffle], 2);\n    uClock.setShuffleTemplate(shuffle_templates[current_shuffle], 3);\n    // if no multirack support set on setOnStep() then the suffle is global\n    //uClock.setShuffleTemplate(shuffle_templates[current_shuffle]);\n    // enable/disable shuffle for specific tracks\n    uClock.setShuffle(true, 0);\n    uClock.setShuffle(true, 1);\n    uClock.setShuffle(true, 2);\n    uClock.setShuffle(true, 3);\n    \n    uClock.start();\n}\n```\n\n#### Shuffle API Reference\n\n```cpp\n// Enable/disable shuffle for a track\nvoid setShuffle(bool active, uint8_t track = 0);\n\n// Check if shuffle is active\nbool isShuffled(uint8_t track = 0);\n\n// Set shuffle pattern size (max 16 steps)\nvoid setShuffleSize(uint8_t size, uint8_t track = 0);\n\n// Set individual shuffle values\nvoid setShuffleData(uint8_t step, int8_t tick, uint8_t track = 0);\n\n// Set complete shuffle template\nvoid setShuffleTemplate(int8_t* shuff, uint8_t size, uint8_t track = 0);\n\n// Get shuffle offset for note length compensation\nint8_t getShuffleLength(uint8_t track = 0);\n```\n\n**Shuffle Values**:\n- Range: `-(output_ppqn/4)-1` to `+(output_ppqn/4)-1` ticks\n- Negative values = play earlier\n- Positive values = delay\n- Zero = no shuffle (straight timing)\n\n### External Sync and Phase Lock\n\nFine-tune synchronization with external clocks:\n\n```cpp\nvoid setup() {\n    // sets external clock\n    uClock.setClockMode(uClock.EXTERNAL_CLOCK);\n    // get back to internal clock\n    //uClock.setClockMode(uClock.INTERNAL_CLOCK);\n\n    // Lock phase every N quarters (default: 1)\n    // Higher values = looser sync but more stable\n    // Lower values = tighter sync but may jitter\n    uClock.setPhaseLockQuartersCount(1);\n\n    // Smooth tempo reading for uClock.getTempo()\n    // Buffer size: 1-254 (larger = smoother but slower lock time response)\n    // The higher the value the longer it takes to sync getTempo() value to actual tempo\n    // PS: the tempo taken to bpm sync here it is for getTempo() value only\n    // it does not affect the main clock.\n    uClock.setExtIntervalBuffer(64);\n}\n```\n\n## Configuration API\n\n### Clock Control\n\n```cpp\n// Set internal tempo (1-500 BPM)\nvoid setTempo(float bpm);\n\n// Get current tempo (works with external sync too)\nfloat getTempo();\n\n// Clock transport control\nvoid start();        // Start clock\nvoid stop();         // Stop clock\nvoid pause();        // Toggle pause/continue\n\n// Clock mode\nvoid setClockMode(ClockMode mode);\nClockMode getClockMode();\n// Modes: INTERNAL_CLOCK, EXTERNAL_CLOCK\n```\n\n### Resolution Configuration\n\n```cpp\n// Set main output resolution\nvoid setOutputPPQN(PPQNResolution resolution);\n\n// Set expected input resolution (external sync)\nvoid setInputPPQN(PPQNResolution resolution);\n\n// Available resolutions:\n// PPQN_1, PPQN_2, PPQN_4, PPQN_8, PPQN_12, PPQN_24\n// PPQN_48, PPQN_96, PPQN_384, PPQN_480, PPQN_960\n```\n\n**Important**: `inputPPQN` must be ≤ `outputPPQN`\n\n### Timing Utilities\n\n```cpp\n// Convert BPM to microseconds\nuint32_t bpmToMicroSeconds(float bpm);\n\n// Elapsed time functions\nuint8_t getNumberOfSeconds(uint32_t time);\nuint8_t getNumberOfMinutes(uint32_t time);\nuint8_t getNumberOfHours(uint32_t time);\nuint8_t getNumberOfDays(uint32_t time);\n\n// Timers\nuint32_t getNowTimer();\nuint32_t getPlayTime();\n```\n\n## Software Timer Mode\n\nFor unsupported boards or to avoid interrupt conflicts, uClock can run in software mode:\n\n**Automatic Fallback**: Activates automatically on unsupported boards\n\n**Manual Activation**: Define `USE_UCLOCK_SOFTWARE_TIMER` build flag\n\n```cpp\nvoid loop() {\n    // REQUIRED in software timer mode\n    uClock.run();\n\n    // Part of your code here\n    // ...\n    \n    // Call run() frequently for best accuracy\n    uClock.run();\n\n    // Other parts of your code here\n    // ...\n\n    uClock.run();\n}\n```\n\n⚠️ **Note**: Software timer mode provides less accurate timing than hardware interrupts.\n\n## Migration Guide (v1.x → v2.3)\n\n### Breaking Changes\n\n| Old API (v1.x) | New API (v2.3+) |\n|----------------|-----------------|\n| `setClock96PPQNOutput()` | `setOnOutputPPQN()` |\n| `setClock16PPQNOutput()` | `setOnStep()` |\n| `setOnClockStartOutput()` | `setOnClockStart()` |\n| `setOnClockStopOutput()` | `setOnClockStop()` |\n| `setOnSync24()` | `setOnSync(uClock.PPQN_24, onSync24)` |\n| `setOnSync48()` | `setOnSync(uClock.PPQN_48, onSync48)` |\n| `setOnSyncXX()` | `setOnSync(uClock.PPQN_XX, onSyncXX)` |\n\n### Resolution Changes\n\nv2.0 introduces flexible PPQN configuration. If you relied on the old 96 PPQN default:\n\n```cpp\n// Old (implicit 96 PPQN)\nuClock.setClock96PPQNOutput(callback);\n\n// New (explicit configuration)\nuClock.setOutputPPQN(uClock.PPQN_96);\nuClock.setOnOutputPPQN(callback);\n```\n\n### Tick Counting\n\nIf you used `setClock16PPQNOutput()` (16th notes), simply replace with `setOnStep()` - no other changes needed.\n\nIf you relied on specific tick values from `setClock96PPQNOutput()`, verify your timing calculations match your chosen PPQN resolution.\n\n## Examples\n\nComplete examples are available in the `examples/` directory:\n\n- **BasicClock**: Simple internal clock setup\n- **ExternalSync**: Slave to external clock\n- **MidiClockSync**: Full MIDI sync box implementation\n- **AcidStepSequencer**: TB-303 style sequencer engine\n- **MultiTrackSequencer**: Independent track sequencing with shuffle\n\n## Technical Details\n\n### Concurrency Safety\n\nuClock operates at interruption or thread(when avaliable) level, wich means your microcontroller will be executing uClock callbacks processing detached from your main `loop()` code, that creates a concurrency programming context where you need to properly protect read and write access to any shared resource between uClock callback code and `loop()` code.\n\nAll clock operations that involve shared variables or resources use atomic operations to ensure concurrency-safe access between interrupt/threads contexts and your main `loop()` code.\n\n* interruption/thread: Depending on micrcontroller platform we levarage a FreeRTOS implementation when multi core avaliable. In this scenario, a threaded context will be used for callbacks instead of interruption.\n\n#### Understanding Shared Resources\n\n**What are shared variables or resources?**\n\nAny data that is accessed both inside uClock callbacks (which run in interrupt/thread context) and inside your `loop()` function (which runs in the main program context) is considered a shared resource.\n\n**Examples of shared resources:**\n- Pattern data arrays modified in `loop()` and read in `onStepCallback()`\n- Sequencer state variables (mute, arp, etc.) modified in `loop()` and read in `onStepCallback()`\n- MIDI buffers or output queues used in `loop()` and `onStepCallback()`\n\n#### Safe Modification Using ATOMIC\n\nWhen modifying shared variables from your main code, always use the `ATOMIC()` macro:\n```cpp\nvoid onStepCallback(uint32_t step) {\n    // Read pattern state in interrupt context\n    if (!sequencer.mute) {\n        playNote();\n    }\n}\n\n// Example: Safely updating sequencer state from user input\nvoid loop() {\n    if (buttonPressed()) {\n        uint8_t mute = readButton();\n        \n        ATOMIC(\n            sequencer.mute = mute;\n        )\n    }\n}\n```\n```cpp\n// Example: Safely modifying pattern data\nuint8_t pattern[16] = {1,0,0,0, 1,0,0,0, 1,0,0,0, 1,0,0,0};\n\nvoid onStepCallback(uint32_t step) {\n    // Read pattern in interrupt context\n    if (pattern[step % 16]) {\n        playNote();\n    }\n}\n\nvoid loop() {\n    if (userEditedPattern()) {\n        // Safely modify from main loop\n        ATOMIC(\n            pattern[4] = 1;\n            pattern[8] = 0;\n        )\n    }\n}\n```\n\n#### When to Use ATOMIC\n\n**You MUST use ATOMIC when:**\n- Modifying variables at `loop()` that are readed inside uClock callbacks\n- Updating multiple related variables that must change together atomically at `loop()`\n- Writing or Reading variables that are `volatile` (often used with interrupts/threads) at `loop()`\n\n**You DON'T need ATOMIC when:**\n- Coding inside any uClock callback\n- Using uClock's built-in methods like `setTempo()`, `start()`, `stop()` (all uClock public API are already protected)\n\n#### Built-in Thread Safety\n\nuClock's API methods are already interrupt/thread-safe and handle atomic operations internally:\n```cpp\n// These are SAFE to call from loop() without ATOMIC\nuClock.setTempo(120.0);\nuClock.start();\nuClock.stop();\nuClock.pause();\nuClock.setShuffle(true, 0);\nuClock.setShuffleTemplate(template, 16, 0);\n```\n\n#### Common Pitfalls\n\n**❌ Wrong - Race condition:**\n```cpp\nuint8_t stepCount = 0;\n\nvoid onStepCallback(uint32_t step) {\n    stepCount++;  // Modified in interrupt/thread context\n}\n\nvoid loop() {\n    if (stepCount >= 16) {  // Read in main loop - NOT ATOMIC!\n        stepCount = 0;      // Modified in main loop - DANGER!\n        doSomething();\n    }\n}\n```\n\n**✅ Correct - Atomic access:**\n```cpp\nvolatile uint8_t stepCount = 0;\n\nvoid onStepCallback(uint32_t step) {\n    stepCount++;  // Modified in interrupt/thread context\n}\n\nvoid loop() {\n    uint8_t currentCount;\n    \n    ATOMIC(\n        currentCount = stepCount;  // Atomic read\n    )\n    \n    if (currentCount >= 16) {\n        ATOMIC(\n            stepCount = 0;  // Atomic write\n        )\n        doSomething();\n    }\n}\n```\n\n#### Advanced: Platform-Specific Implementation\n\nFor AVR platforms, `ATOMIC()` is defined as:\n```cpp\n#define ATOMIC(X) noInterrupts(); X; interrupts();\n```\n\nFor some ARM's and other platforms with multi-core architecture, uClock uses platform-appropriate atomic mechanisms. \n\nFrom user perspective the ATOMIC() macro is transparent and safe to use when you need, independently from you micro-controller architecture.\n\n**Note:** Keep atomic sections as short as possible to minimize interrupt latency. Only include the actual shared variable access inside the `ATOMIC()` block:\n```cpp\n// Good - minimal atomic section\nATOMIC(pattern = newPattern;)\nupdateDisplay(pattern);  // Outside atomic section\n\n// Less ideal - unnecessary code in atomic section\nATOMIC(\n    pattern = newPattern;\n    updateDisplay(pattern);  // This doesn't need to be atomic!\n)\n```\n\n## License\n\nMIT License - Copyright (c) 2025 Romulo Silva\n\n## Support & Community\n\n- **Documentation**: [GitHub Repository](https://github.com/midilab/uClock)\n- **Issues**: Report bugs via GitHub Issues\n- **Contact**: contact@midilab.co\n\n---\n\n**uClock** - Precision timing for creative minds.\n"
  },
  {
    "path": "examples/AVRUartSlaveMidiClockMonitor/AVRUartSlaveMidiClockMonitor.ino",
    "content": "/* Uart MIDI Sync Slave Box Monitor\n *\n * This example demonstrates how to create a\n * MIDI slave clock box with\n * monitor support using oled display\n *\n * MIDI in must be provided via an opto-isolator to pin RX/D0\n * Tested on an Arduino Uno.\n *\n * You need the following libraries to make it work\n * - Midi Library\n * - u8g2\n * - uClock\n * This example code is in the public domain.\n *\n * Code by midilab contact@midilab.co\n * Example modified by Jackson Devices contact@jacksondevices.com\n */\n#include <MIDI.h>\n#include <U8x8lib.h>\n\n//\n// BPM Clock support\n//\n#include <uClock.h>\n\nMIDI_CREATE_DEFAULT_INSTANCE();\nU8X8 * u8x8;\n\n// MIDI clock, start, stop, note on and note off byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n\nfloat bpm = 126.0;\nuint8_t bpm_blink_timer = 1;\nuint8_t clock_state = 1;\nuint8_t clock_mode = 0;\n\nvoid handle_bpm_led(uint32_t tick) {\n  // BPM led indicator\n  if (!(tick % (96)) || (tick == 1)) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if (!(tick % (24))) {  // each quarter led on\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if (!(tick % bpm_blink_timer)) {  // get led off\n    digitalWrite(LED_BUILTIN, LOW);\n    bpm_blink_timer = 1;\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  MIDI.sendRealTime(MIDI_CLOCK);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  MIDI.sendRealTime(MIDI_START);\n}\n\nvoid onClockStop() {\n  MIDI.sendRealTime(MIDI_STOP);\n  digitalWrite(LED_BUILTIN, LOW);\n}\n\n// External clock handlers\nvoid onExternalClock() {\n  uClock.clockMe();\n}\n\nvoid onExternalStart() {\n  uClock.start();\n}\n\nvoid onExternalStop() {\n  uClock.stop();\n  digitalWrite(LED_BUILTIN, LOW);\n}\n\nvoid setup() {\n  //\n  // MIDI setup\n  //\n  // Initiate MIDI communications, listen to all channels, disable soft MIDI thru\n  MIDI.begin();\n  MIDI.turnThruOff();\n  MIDI.setHandleClock(onExternalClock);\n  MIDI.setHandleStart(onExternalStart);\n  MIDI.setHandleStop(onExternalStop);\n\n  // An led to display MIDI reception\n  pinMode(LED_BUILTIN, OUTPUT);\n  digitalWrite(LED_BUILTIN, LOW);\n\n  //\n  // OLED setup\n  // Please check you oled model to correctly init him\n  // The complete list is available here: https://github.com/olikraus/u8g2/wiki/u8g2setupcpp\n  //\n  //u8x8 = new U8X8_SH1106_128X64_NONAME_HW_I2C(U8X8_PIN_NONE);\n  u8x8 = new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE);\n  u8x8->begin();\n  u8x8->setFont(u8x8_font_pressstart2p_r);\n  u8x8->clear();\n  u8x8->setFlipMode(true);\n  u8x8->drawUTF8(0, 0, \"uClock\");\n\n  //\n  // uClock Setup\n  //\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // For MIDI Sync Start and Stop\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n  uClock.setClockMode(uClock.EXTERNAL_CLOCK);\n  // for smooth slave tempo calculate display you should raise the\n  // buffer_size of ext_interval_buffer in between 64 to 128. 254 max size.\n  // note: this doesn't impact on sync time, only display time getTempo()\n  // if you dont want to use it, it is default set it to 1 for memory save\n  uClock.setExtIntervalBuffer(128);\n\n  // inits uClock\n  uClock.init();\n\n  //uClock.setTempo(136.5);\n  //uClock.start();\n}\n\nvoid loop() {\n  while(MIDI.read()) {}\n  // DO NOT ADD MORE PROCESS HERE AT THE COST OF LOSING CLOCK SYNC\n  // Since arduino make use of Serial RX interruption we need to\n  // read Serial as fast as we can on the loop\n  if (bpm != uClock.getTempo()) {\n    bpm = uClock.getTempo();\n    u8x8->drawUTF8(8, 7, String(bpm, 1).c_str());\n    u8x8->drawUTF8(8 + 5, 7, \"BPM\");\n    // clear display ghost number for 2 digit\n    // coming from 3 digit bpm changes\n    if (bpm < 100) {\n      u8x8->drawUTF8(8 + 4, 7, \" \");\n    }\n  }\n  if (clock_state != uClock.clock_state) {\n    clock_state = uClock.clock_state;\n    if (clock_state >= 1) {\n      u8x8->drawUTF8(0, 7, \"Playing\");\n    } else {\n      u8x8->drawUTF8(0, 7, \"Stopped \");\n    }\n  }\n  if (clock_mode != uClock.getClockMode()) {\n    clock_mode = uClock.getClockMode();\n    if (clock_mode == uClock.EXTERNAL_CLOCK) {\n      u8x8->drawUTF8(10, 0, \"Slave \");\n    } else {\n      u8x8->drawUTF8(10, 0, \"Master\");\n    }\n  }\n}\n"
  },
  {
    "path": "examples/AcidStepSequencer/AcidStepSequencer.ino",
    "content": "// Acid StepSequencer, a Roland TB303 step sequencer engine clone\n// author: midilab contact@midilab.co\n// under MIT license\n#include \"Arduino.h\"\n#include <uClock.h>\n\n// Sequencer config\n#define STEP_MAX_SIZE      16\n#define NOTE_LENGTH        12 // min: 1 max: 23 DO NOT EDIT BEYOND!!! 12 = 50% on 96ppqn, same as original tb303. 62.5% for triplets time signature\n#define NOTE_VELOCITY      90\n#define ACCENT_VELOCITY    127\n\n// MIDI modes\n#define MIDI_CHANNEL      0 // 0 = channel 1\n#define MIDI_MODE\n//#define SERIAL_MODE\n\n// do not edit from here!\n#define NOTE_STACK_SIZE    3\n\n// MIDI clock, start, stop, note on and note off byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n#define NOTE_ON    0x90\n#define NOTE_OFF   0x80\n\n// Sequencer data\ntypedef struct\n{\n  uint8_t note;\n  bool accent;\n  bool glide;\n  bool rest;\n} SEQUENCER_STEP_DATA;\n\ntypedef struct\n{\n  uint8_t note;\n  int8_t length;\n} STACK_NOTE_DATA;\n\n// main sequencer data\nSEQUENCER_STEP_DATA _sequencer[STEP_MAX_SIZE];\nSTACK_NOTE_DATA _note_stack[NOTE_STACK_SIZE];\nuint16_t _step_length = STEP_MAX_SIZE;\n\n// make sure all above sequencer data are modified atomicly only\n// eg. ATOMIC(_sequencer[0].accent = true); ATOMIC(_step_length = 7);\n#define ATOMIC(X) noInterrupts(); X; interrupts();\n\n// shared data to be used for user interface feedback\nbool _playing = false;\nuint16_t _step = 0;\n\nvoid sendMidiMessage(uint8_t command, uint8_t byte1, uint8_t byte2)\n{\n  // send midi message\n  command = command | (uint8_t)MIDI_CHANNEL;\n  Serial.write(command);\n  Serial.write(byte1);\n  Serial.write(byte2);\n}\n\n// Each call represents exactly one step.\nvoid onStepCallback(uint32_t tick)\n{\n  uint16_t step;\n  uint16_t length = NOTE_LENGTH;\n\n  // get actual step.\n  _step = tick % _step_length;\n\n  // send note on only if this step are not in rest mode\n  if ( _sequencer[_step].rest == false ) {\n\n    // check for glide event ahead of _step\n    step = _step;\n    for ( uint16_t i = 1; i < _step_length; i++  ) {\n      ++step;\n      step = step % _step_length;\n      if ( _sequencer[step].glide == true && _sequencer[step].rest == false ) {\n        length = NOTE_LENGTH + (i * 24);\n        break;\n      } else if ( _sequencer[step].rest == false ) {\n        break;\n      }\n    }\n\n    // find a free note stack to fit in\n    for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) {\n      if ( _note_stack[i].length == -1 ) {\n        _note_stack[i].note = _sequencer[_step].note;\n        _note_stack[i].length = length;\n        // send note on\n        sendMidiMessage(NOTE_ON, _sequencer[_step].note, _sequencer[_step].accent ? ACCENT_VELOCITY : NOTE_VELOCITY);\n        return;\n      }\n    }\n  }\n}\n\n// The callback function wich will be called by uClock each Pulse of 96PPQN clock resolution.\nvoid onOutputPPQNCallback(uint32_t tick)\n{\n  // handle note on stack\n  for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) {\n    if ( _note_stack[i].length != -1 ) {\n      --_note_stack[i].length;\n      if ( _note_stack[i].length == 0 ) {\n        sendMidiMessage(NOTE_OFF, _note_stack[i].note, 0);\n        _note_stack[i].length = -1;\n      }\n    }\n  }\n\n  // user feedback about sequence time events\n  tempoInterface(tick);\n}\n\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  Serial.write(MIDI_CLOCK);\n}\n\n// The callback function wich will be called when clock starts by using Clock.start() method.\nvoid onClockStart()\n{\n  Serial.write(MIDI_START);\n  _playing = true;\n}\n\n// The callback function wich will be called when clock stops by using Clock.stop() method.\nvoid onClockStop()\n{\n  Serial.write(MIDI_STOP);\n  // send all note off on sequencer stop\n  for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) {\n    sendMidiMessage(NOTE_OFF, _note_stack[i].note, 0);\n    _note_stack[i].length = -1;\n  }\n  _playing = false;\n}\n\nvoid setup()\n{\n  // Initialize serial communication\n#ifdef MIDI_MODE\n  // the default MIDI serial speed communication at 31250 bits per second\n  Serial.begin(31250);\n#endif\n#ifdef SERIAL_MODE\n  // for usage with a PC with a serial to MIDI bridge\n  Serial.begin(115200);\n#endif\n\n  // Set the callback function for the clock output to send MIDI Sync message.\n  uClock.setOnOutputPPQN(onOutputPPQNCallback);\n\n  // for MIDI sync\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n\n  // Set the callback function for the step sequencer on 16ppqn\n  uClock.setOnStep(onStepCallback);\n\n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // Set the clock BPM to 126 BPM\n  uClock.setTempo(126);\n\n  // initing sequencer data\n  for ( uint16_t i = 0; i < STEP_MAX_SIZE; i++ ) {\n    _sequencer[i].note = 48;\n    _sequencer[i].accent = false;\n    _sequencer[i].glide = false;\n    _sequencer[i].rest = false;\n  }\n\n  // initing note stack data\n  for ( uint8_t i = 0; i < NOTE_STACK_SIZE; i++ ) {\n    _note_stack[i].note = 0;\n    _note_stack[i].length = -1;\n  }\n\n  // pins, buttons, leds and pots config\n  configureInterface();\n}\n\n// User interaction goes here\nvoid loop()\n{\n  processInterface();\n}\n"
  },
  {
    "path": "examples/AcidStepSequencer/DefaultUserInterface.ino",
    "content": "\n#define SEQUENCER_MIN_BPM  50\n#define SEQUENCER_MAX_BPM  177\n\n// Ui config\n#define LOCK_POT_SENSTIVITY 3\n\n// hardware setup to fit different kinda of setups and arduino models\n#define OCTAVE_POT_PIN            A3\n#define NOTE_POT_PIN              A2\n#define STEP_LENGTH_POT_PIN       A1\n#define TEMPO_POT_PIN             A0\n\n#define PREVIOUS_STEP_BUTTON_PIN  2\n#define NEXT_STEP_BUTTON_PIN      3\n#define REST_BUTTON_PIN           4\n#define GLIDE_BUTTON_PIN          5\n#define ACCENT_BUTTON_PIN         6\n#define PLAY_STOP_BUTTON_PIN      7\n\n#define PREVIOUS_STEP_LED_PIN     8\n#define NEXT_STEP_LED_PIN         9\n#define REST_LED_PIN              10\n#define GLIDE_LED_PIN             11\n#define ACCENT_LED_PIN            12\n#define PLAY_STOP_LED_PIN         13\n\n// User Interface data\nuint16_t _step_edit = 0;\nuint8_t _last_octave = 3;\nuint8_t _last_note = 0;\n\nuint8_t _bpm_blink_timer = 1;\n\nvoid configureInterface()\n{\n  // Buttons config\n  // use internal pullup for buttons\n  pinMode(PREVIOUS_STEP_BUTTON_PIN, INPUT_PULLUP);\n  pinMode(NEXT_STEP_BUTTON_PIN, INPUT_PULLUP);\n  pinMode(REST_BUTTON_PIN, INPUT_PULLUP);\n  pinMode(GLIDE_BUTTON_PIN, INPUT_PULLUP);\n  pinMode(ACCENT_BUTTON_PIN, INPUT_PULLUP);\n  pinMode(PLAY_STOP_BUTTON_PIN, INPUT_PULLUP);\n\n  // Leds config\n  pinMode(PREVIOUS_STEP_LED_PIN, OUTPUT);\n  pinMode(NEXT_STEP_LED_PIN, OUTPUT);\n  pinMode(REST_LED_PIN, OUTPUT);\n  pinMode(GLIDE_LED_PIN, OUTPUT);\n  pinMode(ACCENT_LED_PIN, OUTPUT);\n  pinMode(PLAY_STOP_LED_PIN, OUTPUT);\n\n  digitalWrite(PREVIOUS_STEP_LED_PIN, LOW);\n  digitalWrite(NEXT_STEP_LED_PIN, LOW);\n  digitalWrite(REST_LED_PIN, LOW);\n  digitalWrite(GLIDE_LED_PIN, LOW);\n  digitalWrite(ACCENT_LED_PIN, LOW);\n  digitalWrite(PLAY_STOP_LED_PIN, LOW);  \n\n  // getting first value state\n  pressed(PREVIOUS_STEP_BUTTON_PIN);\n  pressed(NEXT_STEP_BUTTON_PIN);\n  pressed(REST_BUTTON_PIN);\n  pressed(GLIDE_BUTTON_PIN);\n  pressed(ACCENT_BUTTON_PIN);\n  pressed(PLAY_STOP_BUTTON_PIN);\n\n  // getting first values\n  getPotChanges(OCTAVE_POT_PIN, 0, 10);\n  getPotChanges(NOTE_POT_PIN, 0, 11);\n  getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE);\n  getPotChanges(TEMPO_POT_PIN, SEQUENCER_MIN_BPM, SEQUENCER_MAX_BPM);\n  \n  lockPotsState(true);\n\n  //acidRandomize();\n}\n\nvoid processInterface()\n{\n  processButtons();\n  processLeds();\n  processPots();  \n}\n\nvoid tempoInterface(uint32_t tick) \n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 0) ) {  // first compass step will flash longer\n    _bpm_blink_timer = 8;\n    digitalWrite(PLAY_STOP_LED_PIN , HIGH);\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    digitalWrite(PLAY_STOP_LED_PIN , HIGH);\n  } else if ( !(tick % _bpm_blink_timer) ) { // get led off\n    digitalWrite(PLAY_STOP_LED_PIN , LOW);\n    _bpm_blink_timer = 1;\n  }\n}\n\nvoid sendPreviewNote(uint16_t step)\n{\n  unsigned long milliTime, preMilliTime;\n  \n  sendMidiMessage(NOTE_ON, _sequencer[step].note, _sequencer[step].accent ? ACCENT_VELOCITY : NOTE_VELOCITY);\n\n  // avoid delay() call because of uClock timmer1 usage\n  //delay(200);\n  preMilliTime = millis();\n  while ( true ) {\n    milliTime = millis();\n    if (abs(milliTime - preMilliTime) >= 200) {\n      break;\n    }\n  }\n  \n  sendMidiMessage(NOTE_OFF, _sequencer[step].note, 0);\n}\n\nvoid processPots()\n{\n  static int8_t octave, note, step_note;\n  static int16_t tempo, step_length;\n\n  octave = getPotChanges(OCTAVE_POT_PIN, 0, 10);\n  if ( octave != -1 ) {  \n    _last_octave = octave;\n  }\n\n  note = getPotChanges(NOTE_POT_PIN, 0, 11);\n  if ( note != -1 ) { \n    _last_note = note;\n  }\n\n  // changes on octave or note pot?\n  if ( octave != -1 || note != -1 ) {\n    ATOMIC(_sequencer[_step_edit].note = (_last_octave * 8) + _last_note);\n    if ( _playing == false && _sequencer[_step_edit].rest == false ) {\n      sendPreviewNote(_step_edit);\n    }\n  }\n\n  step_length = getPotChanges(STEP_LENGTH_POT_PIN, 1, STEP_MAX_SIZE);\n  if ( step_length != -1 ) {  \n    ATOMIC(_step_length = step_length);\n    if ( _step_edit >= _step_length ) {\n      _step_edit = _step_length-1;\n    }\n  }\n\n  tempo = getPotChanges(TEMPO_POT_PIN, SEQUENCER_MIN_BPM, SEQUENCER_MAX_BPM);\n  if ( tempo != -1 ) {   \n    //uClock.setTempo(tempo);\n  }\n}\n  \nvoid processButtons()\n{\n  // play/stop\n  if ( pressed(PLAY_STOP_BUTTON_PIN) ) {\n    if ( _playing == false ) {\n      // Starts the clock, tick-tac-tick-tac...\n      uClock.start();\n    } else {\n      // stop the clock\n      uClock.stop();\n    }\n  }\n\n  // ramdom test\n  //if ( pressed(PREVIOUS_STEP_BUTTON_PIN) && pressed(NEXT_STEP_BUTTON_PIN) ) {\n    //acidRandomize();\n    //return;\n  //}\n\n  // previous step edit\n  if ( pressed(PREVIOUS_STEP_BUTTON_PIN) ) {\n    if ( _step_edit != 0 ) {\n      // add a lock here for octave and note to not mess with edit mode when moving steps around \n      lockPotsState(true);   \n      --_step_edit;\n    } else { // TODO: just for tests.. take this guy off here and put it on second page\n      acidRandomize();\n    }\n    if ( _playing == false && _sequencer[_step_edit].rest == false ) {\n      sendPreviewNote(_step_edit);\n    }\n  }\n\n  // next step edit\n  if ( pressed(NEXT_STEP_BUTTON_PIN) ) {\n    if ( _step_edit < _step_length-1 ) {\n      // add a lock here for octave and note to not mess with edit mode when moving steps around\n      lockPotsState(true);     \n      ++_step_edit;\n    }\n    if ( _playing == false && _sequencer[_step_edit].rest == false ) {\n      sendPreviewNote(_step_edit);\n    }    \n  }\n\n  // step rest\n  if ( pressed(REST_BUTTON_PIN) ) {\n    ATOMIC(_sequencer[_step_edit].rest = !_sequencer[_step_edit].rest);\n    if ( _playing == false && _sequencer[_step_edit].rest == false ) {\n      sendPreviewNote(_step_edit);\n    }\n  }\n\n  // step glide\n  if ( pressed(GLIDE_BUTTON_PIN) ) {\n    ATOMIC(_sequencer[_step_edit].glide = !_sequencer[_step_edit].glide);\n  }\n\n  // step accent\n  if ( pressed(ACCENT_BUTTON_PIN) ) {\n    ATOMIC(_sequencer[_step_edit].accent = !_sequencer[_step_edit].accent);\n    if ( _playing == false && _sequencer[_step_edit].rest == false ) {\n      sendPreviewNote(_step_edit);\n    }       \n  }     \n}\n  \nvoid processLeds()\n{   \n  // Editing First Step? \n  if ( _step_edit == 0 ) {\n    digitalWrite(PREVIOUS_STEP_LED_PIN , HIGH);\n  } else {\n    digitalWrite(PREVIOUS_STEP_LED_PIN , LOW);\n  }  \n\n  // Editing Last Step? \n  if ( _step_edit == _step_length-1 ) {\n    digitalWrite(NEXT_STEP_LED_PIN , HIGH);\n  } else {\n    digitalWrite(NEXT_STEP_LED_PIN , LOW);\n  }  \n  \n  // Rest \n  if ( _sequencer[_step_edit].rest == true ) {\n    digitalWrite(REST_LED_PIN , HIGH);\n  } else {\n    digitalWrite(REST_LED_PIN , LOW);\n  }\n\n  // Glide \n  if ( _sequencer[_step_edit].glide == true ) {\n    digitalWrite(GLIDE_LED_PIN , HIGH);\n  } else {\n    digitalWrite(GLIDE_LED_PIN , LOW);\n  }  \n\n  // Accent \n  if ( _sequencer[_step_edit].accent == true ) {\n    digitalWrite(ACCENT_LED_PIN , HIGH);\n  } else {\n    digitalWrite(ACCENT_LED_PIN , LOW);\n  } \n\n  // shut down play led if we are stoped\n  if ( _playing == false ) {\n    digitalWrite(PLAY_STOP_LED_PIN , LOW);\n  }\n}\n\nvoid acidRandomize() \n{\n  // ramdom it all\n  for ( uint16_t i = 0; i < STEP_MAX_SIZE; i++ ) {\n    ATOMIC(_sequencer[i].note = random(36, 70)); // octave 2 to 4. octave 3 to 5 (40 - 83)\n    ATOMIC(_sequencer[i].accent = random(0, 2));\n    ATOMIC(_sequencer[i].glide = random(0, 2));\n    ATOMIC(_sequencer[i].rest = random(0, 1));\n  }\n}\n"
  },
  {
    "path": "examples/AcidStepSequencer/HardwareInterface.ino",
    "content": "\n#define POT_NUMBER    4\n#define BUTTON_NUMBER 6\n\n// pot data\ntypedef struct\n{\n  uint8_t pin;\n  uint16_t state;\n  bool lock;\n} POT_DATA;\n\n// button data\ntypedef struct\n{\n  uint8_t pin;\n  bool state;\n} BUTTON_DATA;\n\nPOT_DATA _pot[POT_NUMBER];\nBUTTON_DATA _button[BUTTON_NUMBER];\n\nvoid lockPotsState(bool lock)\n{\n  for ( uint8_t i = 0; i < POT_NUMBER; i++ ) {\n    _pot[i].lock = lock;\n  }\n}\n\nbool pressed(uint8_t button_pin)\n{\n  bool value;\n  bool * last_value;\n\n  switch(button_pin) {\n    case PREVIOUS_STEP_BUTTON_PIN:\n      last_value = &_button[0].state;\n      break;\n    case NEXT_STEP_BUTTON_PIN:\n      last_value = &_button[1].state;\n      break;\n    case REST_BUTTON_PIN:\n      last_value = &_button[2].state;\n      break;   \n    case GLIDE_BUTTON_PIN:\n      last_value = &_button[3].state;\n      break;\n    case ACCENT_BUTTON_PIN:\n      last_value = &_button[4].state;\n      break;\n    case PLAY_STOP_BUTTON_PIN:\n      last_value = &_button[5].state;\n      break;    \n    default:\n      return false;                    \n  }\n  \n  value = digitalRead(button_pin);\n  \n  // check, using pullup pressed button goes LOW\n  if ( value != *last_value && value == LOW ) {\n    *last_value = value; \n    return true;    \n  } else {\n    *last_value = value; \n    return false;\n  }\n   \n}\n\nint16_t getPotChanges(uint8_t pot_pin, uint16_t min_value, uint16_t max_value)\n{\n  uint16_t value, value_ranged, last_value_ranged;\n  uint16_t * last_value;\n  bool * lock_pot;\n  uint8_t pot_sensitivity = 1;\n\n  switch(pot_pin) {\n    case OCTAVE_POT_PIN:\n      last_value = &_pot[0].state;\n      lock_pot = &_pot[0].lock;\n      break;\n    case NOTE_POT_PIN:\n      last_value = &_pot[1].state;\n      lock_pot = &_pot[1].lock;\n      break;\n    case STEP_LENGTH_POT_PIN:\n      last_value = &_pot[2].state;\n      lock_pot = &_pot[2].lock;\n      break;   \n    case TEMPO_POT_PIN:\n      last_value = &_pot[3].state;\n      lock_pot = &_pot[3].lock;\n      break;\n    default:\n      return -1;\n  }\n\n  // get absolute value\n  value = analogRead(pot_pin);\n    \n  // range that value and our last_value\n  value_ranged = (value / (1024 / ((max_value - min_value) + 1))) + min_value;\n  last_value_ranged = (*last_value / (1024 / ((max_value - min_value) + 1))) + min_value; \n\n  // a lock system to not mess with some data(pots are terrible for some kinda of user interface data controls, but lets keep it low cost!)\n  if ( *lock_pot == true ) {\n      pot_sensitivity = LOCK_POT_SENSTIVITY;\n  }\n  \n  if ( abs(value_ranged - last_value_ranged) >= pot_sensitivity ) {\n    *last_value = value;\n    if ( *lock_pot == true ) {\n      *lock_pot = false;\n    }\n    return value_ranged;    \n  } else {\n    return -1;\n  }  \n}\n"
  },
  {
    "path": "examples/AcidStepSequencer/README.md",
    "content": "# TB303 Step Sequencer engine clone\n\nWith some user interface changes for cheap construction of a functional TB303 engine we present you the interface:\n\n![Image of uMODULAR ctrl16 pcb top view](https://raw.githubusercontent.com/midilab/uClock/development/examples/AcidStepSequencer/acid_step_sequencer-protoboard-v001.png)\n\n## Interface from left to rigth\n\nPOT1: Octave\nPOT2: Note\nPOT3: Sequence Length\nPOT4: Sequencer BPM Tempo\n\nButton1: Prev Step\nButton2: Next Step\nButton3: Rest\nButton4: Glide\nButton5: Accent\nButtons6: Play/Stop"
  },
  {
    "path": "examples/ESP32UartMasterMidiClock/ESP32UartMasterMidiClock.ino",
    "content": "/* Uart MIDI Sync Box\n *\n * This example demonstrates how to change the Uart MIDI\n * device name on ESP32 family.\n *\n * This example code is in the public domain.\n *\n * ...\n *\n */\n#include <uClock.h>\n\n// MIDI clock, start and stop byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n\n// the blue led\n#define LED_BUILTIN    2\n\nuint8_t bpm_blink_timer = 1;\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    bpm_blink_timer = 1;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    digitalWrite(LED_BUILTIN, LOW);\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  Serial.write(MIDI_CLOCK);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  Serial.write(MIDI_START);\n}\n\nvoid onClockStop() {\n  Serial.write(MIDI_STOP);\n}\n\nvoid setup() {\n  // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication:\n  Serial.begin(31250);\n\n  // A led to count bpms\n  pinMode(LED_BUILTIN, OUTPUT);\n\n  // Setup our clock system\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // Set the clock BPM to 126 BPM\n  uClock.setTempo(126);\n  // Starts the clock, tick-tac-tick-tac...\n  uClock.start();\n}\n\n// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment...\nvoid loop() {\n\n}\n"
  },
  {
    "path": "examples/GenericMasterOrExternalSync/GenericMasterOrExternalSync.ino",
    "content": "#include <uClock.h>\n\n// external or internal sync?\nbool _external_sync_on = false;\n\n// the main uClock PPQN resolution ticking\nvoid onOutputPPQNCallback(uint32_t tick) {\n  // tick your sequencers or tickable devices...\n}\n\nvoid onStepCallback(uint32_t step) {\n  // triger step data for sequencer device...\n}\n\n// The callback function called by uClock each Pulse of 1PPQN clock resolution.\nvoid onSync1Callback(uint32_t tick) {\n  // send sync signal to...\n}\n\n// The callback function called by uClock each Pulse of 2PPQN clock resolution.\nvoid onSync2Callback(uint32_t tick) {\n  // send sync signal to...\n}\n\n// The callback function called by uClock each Pulse of 4PPQN clock resolution.\nvoid onSync4Callback(uint32_t tick) {\n  // send sync signal to...\n}\n\n// The callback function called by uClock each Pulse of 24PPQN clock resolution.\nvoid onSync24Callback(uint32_t tick) {\n  // send sync signal to...\n}\n\n// The callback function called by uClock each Pulse of 48PPQN clock resolution.\nvoid onSync48Callback(uint32_t tick) {\n  // send sync signal to...\n}\n\n// The callback function called when clock starts by using uClock.start() method.\nvoid onClockStartCallback() {\n  // send start signal to...\n}\n\n// The callback function called when clock stops by using uClock.stop() method.\nvoid onClockStopCallback() {\n  // send stop signal to...\n}\n\nvoid setup() {\n  // setup clock library\n  // avaliable output resolutions\n  // [ uClock.PPQN_4, uClock.PPQN_8, uClock.PPQN_12, uClock.PPQN_24, uClock.PPQN_48, uClock.PPQN_96, uClock.PPQN_384, uClock.PPQN_480, uClock.PPQN_960 ]\n  // not mandatory to call, the default is 96PPQN if not set\n  uClock.setOutputPPQN(uClock.PPQN_96);\n\n  // you need to use at least one!\n  uClock.setOnOutputPPQN(onOutputPPQNCallback);\n  uClock.setOnStep(onStepCallback);\n  // multi sync output signatures avaliable\n  // normaly used by eurorack modular modules\n  uClock.setOnSync(uClock.PPQN_1, onSync1Callback);\n  uClock.setOnSync(uClock.PPQN_2, onSync2Callback);\n  uClock.setOnSync(uClock.PPQN_4, onSync4Callback);\n  // midi sync standard\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // some korg machines does 48ppqn\n  uClock.setOnSync(uClock.PPQN_48, onSync48Callback);\n\n  uClock.setOnClockStart(onClockStartCallback);\n  uClock.setOnClockStop(onClockStopCallback);\n\n  // set external sync mode?\n  if (_external_sync_on) {\n    uClock.setClockMode(uClock.EXTERNAL_CLOCK);\n    // what is the clock of incomming signal to sync with?\n    // not mandatory to call, the default is 24PPQN if not set\n    // avaliable input resolutions -  should be always InputPPQN <= OutputPPQN\n    // [ uClock.PPQN_1, uClock.PPQN_2, uClock.PPQN_4, uClock.PPQN_8, uClock.PPQN_12, uClock.PPQN_24, uClock.PPQN_48, uClock.PPQN_96, uClock.PPQN_384, uClock.PPQN_480, uClock.PPQN_960 ]\n    uClock.setInputPPQN(uClock.PPQN_24);\n  }\n\n  // inits the clock library\n  uClock.init();\n\n  // starts clock\n  uClock.start();\n}\n\nvoid loop() {\n  // do we need to external sync?\n  if (_external_sync_on) {\n    // watch for external sync signal income\n    bool signal_income = true; // your external input signal check will be this condition result\n    if (signal_income) {\n      // at each clockMe call uClock will process and handle external/internal syncronization\n      uClock.clockMe();\n    }\n  }\n}\n"
  },
  {
    "path": "examples/LeonardoUsbSlaveMidiClockMonitor/LeonardoUsbSlaveMidiClockMonitor.ino",
    "content": "/* USB MIDI Sync Slave Box Monitor\n *\n * This example demonstrates how to create a\n * MIDI hid compilant slave clock box with\n * monitor support using oled displays\n *\n * You need the following libraries to make it work\n * - Midi Library\n * - USB-MIDI and MIDIUSB\n * - u8g2\n * - uClock\n * This example code is in the public domain.\n */\n\n#include <USB-MIDI.h>\n#include <U8x8lib.h>\n\n//\n// BPM Clock support\n//\n#include <uClock.h>\n\nUSBMIDI_CREATE_DEFAULT_INSTANCE();\nU8X8 * u8x8;\n\n// MIDI clock, start, stop, note on and note off byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n\nfloat bpm = 126.0;\nuint8_t bpm_blink_timer = 1;\nuint8_t clock_state = 1;\nuint8_t clock_mode = 0;\n\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    TXLED1;\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    TXLED1;\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    TXLED0;\n    bpm_blink_timer = 1;\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  MIDI.sendRealTime(MIDI_CLOCK);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  MIDI.sendRealTime(MIDI_START);\n}\n\nvoid onClockStop() {\n  MIDI.sendRealTime(MIDI_STOP);\n}\n\n// External clock handlers\nvoid onExternalClock()\n{\n  uClock.clockMe();\n}\n\nvoid onExternalStart()\n{\n  uClock.start();\n}\n\nvoid onExternalStop()\n{\n  uClock.stop();\n}\n\nvoid setup() {\n  //\n  // MIDI setup\n  //\n  MIDI.begin();\n  MIDI.setHandleClock(onExternalClock);\n  MIDI.setHandleStart(onExternalStart);\n  MIDI.setHandleStop(onExternalStop);\n\n  //\n  // OLED setup\n  // Please check you oled model to correctly init him\n  //\n  //u8x8 = new U8X8_SH1106_128X64_NONAME_HW_I2C(U8X8_PIN_NONE);\n  u8x8 = new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE);\n  u8x8->begin();\n  u8x8->setFont(u8x8_font_pressstart2p_r);\n  u8x8->clear();\n  u8x8->setFlipMode(true);\n  u8x8->drawUTF8(0, 0, \"uClock\");\n\n  //\n  // uClock Setup\n  //\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // set main clock rate for input(expected sync signal rate) MIDI 24PPQN clock based\n  uClock.setInputPPQN(uClock.PPQN_24);\n  // For MIDI Sync Start and Stop\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n  uClock.setClockMode(uClock.EXTERNAL_CLOCK);\n  // for smooth slave tempo calculate display you should raise the\n  // buffer_size of ext_interval_buffer in between 64 to 128. 254 max size.\n  // note: this doesn't impact on sync time, only display time getTempo()\n  // if you dont want to use it, it is default set it to 1 for memory save\n  uClock.setExtIntervalBuffer(128);\n\n  // init uClock\n  uClock.init();\n\n  //uClock.setTempo(136.5);\n  //uClock.start();\n}\n\nvoid loop() {\n  while(MIDI.read()) {}\n  // DO NOT ADD MORE PROCESS HERE AT THE COST OF LOSING CLOCK SYNC\n  // Since arduino make use of Serial RX interruption we need to\n  // read Serial as fast as we can on the loop\n  if (bpm != uClock.getTempo()) {\n    bpm = uClock.getTempo();\n    u8x8->drawUTF8(8, 7, String(bpm, 1).c_str());\n    u8x8->drawUTF8(8+5, 7, \"BPM\");\n    // clear display ghost number for 2 digit\n    // coming from 3 digit bpm changes\n    if (bpm < 100) {\n      u8x8->drawUTF8(8+4, 7, \" \");\n    }\n  }\n  if (clock_state != uClock.clock_state) {\n    clock_state = uClock.clock_state;\n    if (clock_state >= 1) {\n      u8x8->drawUTF8(0, 7, \"Playing\");\n    } else {\n      u8x8->drawUTF8(0, 7, \"Stopped\");\n    }\n  }\n  if (clock_mode != uClock.getClockMode()) {\n    clock_mode = uClock.getClockMode();\n    if (clock_mode == uClock.EXTERNAL_CLOCK) {\n      u8x8->drawUTF8(10, 0, \"Slave \");\n    } else {\n      u8x8->drawUTF8(10, 0, \"Master\");\n    }\n  }\n}\n"
  },
  {
    "path": "examples/MidiClock/MidiClock.ino",
    "content": "#include \"Arduino.h\"\n#include <uClock.h>\n\n// MIDI clock, start and stop byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n\n// The callback function wich will be called by Clock each Pulse of 24PPQN clock resolution.\nvoid onSync24Callback(uint32_t tick)\n{\n  // Send MIDI_CLOCK to external gears\n  Serial.write(MIDI_CLOCK);\n}\n\n// The callback function wich will be called when clock starts by using Clock.start() method.\nvoid onClockStart()\n{\n  Serial.write(MIDI_START);\n}\n\n// The callback function wich will be called when clock stops by using Clock.stop() method.\nvoid onClockStop()\n{\n  Serial.write(MIDI_STOP);\n}\n\nvoid setup()\n{\n\n  // Initialize serial communication at 31250 bits per second, the default MIDI serial speed communication:\n  Serial.begin(31250);\n\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // Set the clock BPM to 126 BPM\n  uClock.setTempo(126);\n\n  // Starts the clock, tick-tac-tick-tac...\n  uClock.start();\n\n}\n\n// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment...\nvoid loop()\n{\n\n}\n"
  },
  {
    "path": "examples/RP2040UsbUartMasterClock/RP2040UsbUartMasterClock.ino",
    "content": "/*\n * USB/Uart MIDI Sync Box\n *\n * This example code is in the public domain.\n *\n */\n\n#include <Adafruit_TinyUSB.h>\n#include <MIDI.h>\n\n#include <uClock.h>\n\n// Instantiate the MIDI interfaces\nAdafruit_USBD_MIDI usb_midi;\nMIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI_USB);\nMIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);\n\n// Do your rpi 2040 has a ws2812 RGB LED? set the pin!\n// otherwise keep it commented for normal LED_BUILTIN led blinking\n#define WS2812_BUILTIN_LED  16\n\nuint8_t bpm_blink_timer = 1;\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first of 4 quarter pulse will flash longer\n    bpm_blink_timer = 8;\n    ledOn();\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    bpm_blink_timer = 1;\n    ledOn();\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    ledOff();\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  MIDI.sendRealTime(midi::Clock);\n  MIDI_USB.sendRealTime(midi::Clock);\n  // blink tempo\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  MIDI.sendRealTime(midi::Start);\n  MIDI_USB.sendRealTime(midi::Start);\n}\n\nvoid onClockStop() {\n  MIDI.sendRealTime(midi::Stop);\n  MIDI_USB.sendRealTime(midi::Stop);\n}\n\nvoid setup() {\n#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)\n  // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040\n  TinyUSB_Device_Init(0);\n#endif\n\n  // Initialize USB midi stack\n  MIDI_USB.begin(MIDI_CHANNEL_OMNI);\n  // Initialize UART midi stack\n  MIDI.begin(MIDI_CHANNEL_OMNI);\n\n  // Initialize builtin led for clock timer blinking\n  initBlinkLed();\n\n  // Setup our clock system\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // Set the clock BPM to 126 BPM\n  uClock.setTempo(126);\n  // Starts the clock, tick-tac-tick-tac..\n  uClock.start();\n}\n\n// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment...\nvoid loop() {\n  // handle midi input?\n  MIDI.read();\n  MIDI_USB.read();\n}\n"
  },
  {
    "path": "examples/RP2040UsbUartMasterClock/builtin_led.ino",
    "content": "#if defined(WS2812_BUILTIN_LED)\n#include <Adafruit_NeoPixel.h>\n#define NUMPIXELS 1\nAdafruit_NeoPixel pixels(NUMPIXELS, WS2812_BUILTIN_LED, NEO_GRB + NEO_KHZ800);\n#endif\n\n// check the pinage for BUILTIN LED of your model in case LED_BUILTIN wont ligth up\n// this is valid only if you're not using rgb version ws2812 (WS2812_BUILTIN_LED)\n//#define LED_BUILTIN PIN_LED_B\n\nvoid initBlinkLed() {\n#if defined(WS2812_BUILTIN_LED)\n  // use adafruit neo pixel \n  pixels.begin();\n#else\n  // normal led pin\n  pinMode(LED_BUILTIN, OUTPUT);\n#endif\n}\n\nvoid ledOn() {\n#if defined(WS2812_BUILTIN_LED)\n  pixels.setPixelColor(0, pixels.Color(0, 0, 20));\n  pixels.show();  // turn the LED on (HIGH is the voltage level)\n#else\n  digitalWrite(LED_BUILTIN, LOW);\n#endif\n}\n\nvoid ledOff() {\n#if defined(WS2812_BUILTIN_LED)\n  pixels.setPixelColor(0, pixels.Color(0, 0, 0));\n  pixels.show();\n#else\n  digitalWrite(LED_BUILTIN, HIGH);\n#endif\n}"
  },
  {
    "path": "examples/STM32UartMasterMidiClock/STM32UartMasterMidiClock.ino",
    "content": "/* Uart MIDI out\n *\n * This example demonstrates how to send MIDI data via Uart\n * interface on STM32 family.\n *\n * This example code is in the public domain.\n *\n * Requires STM32Duino board manager to be installed.\n *\n * Define HardwareSerial using any available UART/USART.\n * Nucleo boards have UART/USART pins that are used by the ST-LINK interface (unless using solder bridging).\n *\n * Tested on Nucleo-F401RE and Nucleo-F072RB (PA9=D8 PA10=D2 on the Arduino pins)\n *\n * Code by midilab contact@midilab.co\n * Example modified by Jackson Devices contact@jacksondevices.com\n */\n#include <uClock.h>\n\n// MIDI clock, start and stop byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n\nHardwareSerial Serial1(PA10, PA9);\n\nuint8_t bpm_blink_timer = 1;\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    bpm_blink_timer = 1;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    digitalWrite(LED_BUILTIN, LOW);\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gear\n  Serial1.write(MIDI_CLOCK);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n    // Send MIDI_START to external gear\n  Serial1.write(MIDI_START);\n}\n\nvoid onClockStop() {\n    // Send MIDI_STOP to external gear\n  Serial1.write(MIDI_STOP);\n}\n\nvoid setup() {\n  // Initialize Serial1 communication at 31250 bits per second, the default MIDI Serial1 speed communication:\n  Serial1.begin(31250);\n\n  // An led to display BPM\n  pinMode(LED_BUILTIN, OUTPUT);\n\n  // Setup our clock system\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // Set the clock BPM to 126 BPM\n  uClock.setTempo(120);\n  // Starts the clock, tick-tac-tick-tac...\n  uClock.start();\n}\n\n// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment...\nvoid loop() {\n\n}\n"
  },
  {
    "path": "examples/TeensyUsbMasterMidiClock/TeensyUsbMasterMidiClock.ino",
    "content": "/* USB MIDI Sync Box\n *\n * This example demonstrates how to change the USB MIDI\n * device name on Teensy LC, 3.x and 4.x.  When creating more\n * that one MIDI device, custom names are much easier to\n * use when selecting each device in MIDI software on\n * your PC or Mac.  The custom name is in the \"name.c\" tab.\n *\n * Windows and Macintosh systems often cache USB info.\n * After changing the name, you may need to test on a\n * different computer to observe the new name, or take\n * steps to get your operating system to \"forget\" the\n * cached info.  (TODO: wanted... can anyone contribute\n * instructions for these systems)\n *\n * You must select MIDI from the \"Tools > USB Type\" menu\n *\n * This example code is in the public domain.\n */\n\n#include <uClock.h>\n\nuint8_t bpm_blink_timer = 1;\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    digitalWrite(LED_BUILTIN, LOW);\n    bpm_blink_timer = 1;\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  usbMIDI.sendRealTime(usbMIDI.Clock);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  usbMIDI.sendRealTime(usbMIDI.Start);\n}\n\nvoid onClockStop() {\n  usbMIDI.sendRealTime(usbMIDI.Stop);\n}\n\nvoid setup() {\n  // A led to count bpms\n  pinMode(LED_BUILTIN, OUTPUT);\n\n  // Setup our clock system\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // Set the clock BPM to 126 BPM\n  uClock.setTempo(126);\n  // Starts the clock, tick-tac-tick-tac...\n  uClock.start();\n}\n\n// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment...\nvoid loop() {\n\n}\n"
  },
  {
    "path": "examples/TeensyUsbMasterMidiClock/name.c",
    "content": "// To give your project a unique name, this code must be\n// placed into a .c file (its own tab).  It can not be in\n// a .cpp file or your main sketch (the .ino file).\n\n#include \"usb_names.h\"\n\n// Edit these lines to create your own name.  The length must\n// match the number of characters in your custom name.\n\n#define MIDI_NAME   {'u','c','l','o','c','k','_','1'}\n#define MIDI_NAME_LEN  8\n\n// Do not change this part.  This exact format is required by USB.\n\nstruct usb_string_descriptor_struct usb_string_product_name = {\n        2 + MIDI_NAME_LEN * 2,\n        3,\n        MIDI_NAME\n};\n"
  },
  {
    "path": "examples/TeensyUsbSlaveMidiClock/TeensyUsbSlaveMidiClock.ino",
    "content": "/* USB MIDI Sync Slave Box\n *\n * This example demonstrates how to change the USB MIDI\n * device name on Teensy LC, 3.x and 4.x.  When creating more\n * that one MIDI device, custom names are much easier to\n * use when selecting each device in MIDI software on\n * your PC or Mac.  The custom name is in the \"name.c\" tab.\n *\n * Windows and Macintosh systems often cache USB info.\n * After changing the name, you may need to test on a\n * different computer to observe the new name, or take\n * steps to get your operating system to \"forget\" the\n * cached info.  (TODO: wanted... can anyone contribute\n * instructions for these systems)\n *\n * You must select MIDI from the \"Tools > USB Type\" menu\n *\n * This example code is in the public domain.\n */\n\n#include <uClock.h>\n\nuint8_t bpm_blink_timer = 1;\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    digitalWrite(LED_BUILTIN, LOW);\n    bpm_blink_timer = 1;\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears on other port?\n  //usbMIDI.sendRealTime(usbMIDI.Clock);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  //usbMIDI.sendRealTime(usbMIDI.Start);\n}\n\nvoid onClockStop() {\n  //usbMIDI.sendRealTime(usbMIDI.Stop);\n}\n\n// External clock handlers\nvoid onExternalClock()\n{\n  uClock.clockMe();\n}\n\nvoid onExternalStart()\n{\n  uClock.start();\n}\n\nvoid onExternalStop()\n{\n  uClock.stop();\n}\n\nvoid setup() {\n  // A led to count bpms\n  pinMode(LED_BUILTIN, OUTPUT);\n\n  // Setup realtime midi event handlers\n  usbMIDI.setHandleClock(onExternalClock);\n  usbMIDI.setHandleStart(onExternalStart);\n  usbMIDI.setHandleStop(onExternalStop);\n\n  // Setup our clock system\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // set main clock rate for input(expected sync signal rate) MIDI 24PPQN clock based\n  uClock.setInputPPQN(uClock.PPQN_24);\n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // set to external sync mode\n  uClock.setClockMode(uClock.EXTERNAL_CLOCK);\n\n  // Inits the clock\n  uClock.init();\n}\n\nvoid loop() {\n  // Grab all midi data as fast as we can!\n  while (usbMIDI.read()) {}\n}\n"
  },
  {
    "path": "examples/TeensyUsbSlaveMidiClock/name.c",
    "content": "// To give your project a unique name, this code must be\n// placed into a .c file (its own tab).  It can not be in\n// a .cpp file or your main sketch (the .ino file).\n\n#include \"usb_names.h\"\n\n// Edit these lines to create your own name.  The length must\n// match the number of characters in your custom name.\n\n#define MIDI_NAME   {'u','c','l','o','c','k','_','1'}\n#define MIDI_NAME_LEN  8\n\n// Do not change this part.  This exact format is required by USB.\n\nstruct usb_string_descriptor_struct usb_string_product_name = {\n        2 + MIDI_NAME_LEN * 2,\n        3,\n        MIDI_NAME\n};\n"
  },
  {
    "path": "examples/TeensyUsbSlaveMidiClockMonitor/TeensyUsbSlaveMidiClockMonitor.ino",
    "content": "/* USB MIDI Sync Slave Box Monitor\n *\n * This example demonstrates how to create a\n * MIDI hid compilant slave clock box using\n * Teensy LC, 3.x and 4.x with\n * monitor support using oled displays\n *\n * Making use of a 250 usceconds timer to\n * handle MIDI input to avoid jitter on clock\n *\n * You need the following libraries to make it work\n * - u8g2\n * - uClock\n *\n * This example code is in the public domain.\n */\n\n#include <U8x8lib.h>\n\n//\n// BPM Clock support\n//\n#include <uClock.h>\n\nU8X8 * u8x8;\nIntervalTimer teensyTimer;\n\n// MIDI clock, start, stop, note on and note off byte definitions - based on MIDI 1.0 Standards.\n#define MIDI_CLOCK 0xF8\n#define MIDI_START 0xFA\n#define MIDI_STOP  0xFC\n\nfloat bpm = 126;\nuint8_t bpm_blink_timer = 1;\nuint8_t clock_state = 1;\nuint8_t clock_mode = 0;\n\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    digitalWrite(LED_BUILTIN, HIGH);\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    digitalWrite(LED_BUILTIN, LOW);\n    bpm_blink_timer = 1;\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  usbMIDI.sendRealTime(MIDI_CLOCK);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  usbMIDI.sendRealTime(MIDI_START);\n}\n\nvoid onClockStop() {\n  usbMIDI.sendRealTime(MIDI_STOP);\n  digitalWrite(LED_BUILTIN, LOW);\n}\n\n// External clock handlers\nvoid onExternalClock()\n{\n  uClock.clockMe();\n}\n\nvoid onExternalStart()\n{\n  uClock.start();\n}\n\nvoid onExternalStop()\n{\n  uClock.stop();\n}\n\nvoid setup() {\n  // A led to count bpms\n  pinMode(LED_BUILTIN, OUTPUT);\n\n  //\n  // MIDI setup\n  //\n  usbMIDI.begin();\n  usbMIDI.setHandleClock(onExternalClock);\n  usbMIDI.setHandleStart(onExternalStart);\n  usbMIDI.setHandleStop(onExternalStop);\n\n  //\n  // OLED setup\n  // Please check you oled model to correctly init him\n  //\n  //u8x8 = new U8X8_SH1106_128X64_NONAME_HW_I2C(U8X8_PIN_NONE);\n  u8x8 = new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE);\n  u8x8->begin();\n  u8x8->setFont(u8x8_font_pressstart2p_r);\n  u8x8->clear();\n  u8x8->setFlipMode(true);\n  u8x8->drawUTF8(0, 0, \"uClock\");\n\n  //\n  // uClock Setup\n  //\n  // Setup our clock system\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  // set main clock rate for input(expected sync signal rate) MIDI 24PPQN clock based\n  uClock.setInputPPQN(uClock.PPQN_24);\n  // For MIDI Sync Start and Stop\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n  uClock.setClockMode(uClock.EXTERNAL_CLOCK);\n  // for smooth slave tempo calculate display you should raise the\n  // buffer_size of ext_interval_buffer in between 64 to 128. 254 max size.\n  // note: this doesn't impact on sync time, only display time getTempo()\n  // if you dont want to use it, it is default set it to 1 for memory save\n  uClock.setExtIntervalBuffer(128);\n\n  // inits uClock\n  uClock.init();\n\n  // make use of 250us timer to handle midi input sync\n  teensyTimer.begin(handleMidiInput, 250);\n  teensyTimer.priority(80);\n}\n\nvoid handleMidiInput() {\n  while (usbMIDI.read()) {\n  }\n}\n\nvoid loop() {\n  if (bpm != uClock.getTempo()) {\n    bpm = uClock.getTempo();\n    u8x8->drawUTF8(8, 7, String(bpm, 1).c_str());\n    u8x8->drawUTF8(8+5, 7, \"BPM\");\n    // clear display ghost number for 2 digit\n    // coming from 3 digit bpm changes\n    if (bpm < 100) {\n      u8x8->drawUTF8(8+4, 7, \" \");\n    }\n  }\n  if (clock_state != uClock.clock_state) {\n    clock_state = uClock.clock_state;\n    if (clock_state >= 1) {\n      u8x8->drawUTF8(0, 7, \"Playing\");\n    } else {\n      u8x8->drawUTF8(0, 7, \"Stopped\");\n    }\n  }\n  if (clock_mode != uClock.getClockMode()) {\n    clock_mode = uClock.getClockMode();\n    if (clock_mode == uClock.EXTERNAL_CLOCK) {\n      u8x8->drawUTF8(10, 0, \"Slave \");\n    } else {\n      u8x8->drawUTF8(10, 0, \"Master\");\n    }\n  }\n}\n"
  },
  {
    "path": "examples/TeensyUsbSlaveMidiClockMonitor/name.c",
    "content": "// To give your project a unique name, this code must be\n// placed into a .c file (its own tab).  It can not be in\n// a .cpp file or your main sketch (the .ino file).\n\n#include \"usb_names.h\"\n\n// Edit these lines to create your own name.  The length must\n// match the number of characters in your custom name.\n\n#define MIDI_NAME   {'u','c','l','o','c','k','_','1'}\n#define MIDI_NAME_LEN  8\n\n// Do not change this part.  This exact format is required by USB.\n\nstruct usb_string_descriptor_struct usb_string_product_name = {\n        2 + MIDI_NAME_LEN * 2,\n        3,\n        MIDI_NAME\n};\n"
  },
  {
    "path": "examples/XiaoUsbMasterMidiClock/XiaoUsbMasterMidiClock.ino",
    "content": "/* USB MIDI Sync Box\n *\n * This example demonstrates how to change the USB MIDI\n * device name on Seeedstudio XIAO M0.\n *\n * This example code is in the public domain.\n *\n * Tested with Adafruit TinyUSB version 0.10.5\n *\n */\n#include <Adafruit_TinyUSB.h>\n#include <MIDI.h>\n\nAdafruit_USBD_MIDI usb_midi;\nMIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI_USB);\n\n//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);\n#include <uClock.h>\n\nuint8_t bpm_blink_timer = 1;\nvoid handle_bpm_led(uint32_t tick)\n{\n  // BPM led indicator\n  if ( !(tick % (96)) || (tick == 1) ) {  // first compass step will flash longer\n    bpm_blink_timer = 8;\n    digitalWrite(LED_BUILTIN, LOW);\n  } else if ( !(tick % (24)) ) {   // each quarter led on\n    bpm_blink_timer = 1;\n    digitalWrite(LED_BUILTIN, LOW);\n  } else if ( !(tick % bpm_blink_timer) ) { // get led off\n    digitalWrite(LED_BUILTIN, HIGH);\n  }\n}\n\n// Internal clock handlers\nvoid onSync24Callback(uint32_t tick) {\n  // Send MIDI_CLOCK to external gears\n  MIDI_USB.sendRealTime(midi::Clock);\n  handle_bpm_led(tick);\n}\n\nvoid onClockStart() {\n  MIDI_USB.sendRealTime(midi::Start);\n}\n\nvoid onClockStop() {\n  MIDI_USB.sendRealTime(midi::Stop);\n}\n\nvoid setup() {\n  MIDI_USB.begin(MIDI_CHANNEL_OMNI);\n\n  // A led to count bpms\n  pinMode(LED_BUILTIN, OUTPUT);\n\n  // Setup our clock system\n  // Set the callback function for the clock output to send MIDI Sync message based on 24PPQN\n  uClock.setOnSync(uClock.PPQN_24, onSync24Callback);\n  \n  // Set the callback function for MIDI Start and Stop messages.\n  uClock.setOnClockStart(onClockStart);\n  uClock.setOnClockStop(onClockStop);\n\n  // Inits the clock\n  uClock.init();\n\n  // Set the clock BPM to 126 BPM\n  uClock.setTempo(126);\n  // Starts the clock, tick-tac-tick-tac...\n  uClock.start();\n}\n\n// Do it whatever to interface with Clock.stop(), Clock.start(), Clock.setTempo() and integrate your environment...\nvoid loop() {\n\n}\n"
  },
  {
    "path": "library.json",
    "content": "{\n  \"name\": \"uClock\",\n  \"version\": \"2.3.0\",\n  \"description\": \"A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(Teensy, STM32XX, ESP32, Raspberry Pico, Seedstudio XIAO M0 and RP2040)\",\n  \"keywords\": \"bpm, clock, timing, tick, music, generator\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/midilab/uClock.git\"\n  },\n  \"authors\": [\n    {\n      \"name\": \"Romulo Silva\",\n      \"email\": \"contact@midilab.co\",\n      \"url\": \"https://midilab.co\",\n      \"maintainer\": true\n    }\n  ],\n  \"license\": \"MIT\",\n  \"homepage\": \"https://midilab.co/umodular/\",\n  \"headers\": \"uClock.h\",\n  \"dependencies\": {},\n  \"frameworks\": \"Arduino\",\n  \"platforms\": \"atmelavr,atmelmegaavr,espressif32,ststm32,teensy,atmelsam,raspberrypi\"\n}\n"
  },
  {
    "path": "library.properties",
    "content": "name=uClock\nversion=2.3.0\nauthor=Romulo Silva <contact@midilab.co>\nmaintainer=Romulo Silva <contact@midilab.co>\nsentence=BPM clock generator for Arduino platform.\nparagraph=A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(Teensy, STM32XX, ESP32, Raspberry Pico, Seedstudio XIAO M0 and RP2040)\ncategory=Timing\nurl=https://github.com/midilab/uClock\narchitectures=avr,arm,samd,stm32,esp32,rp2040\nincludes=uClock.h\n"
  },
  {
    "path": "src/platforms/avr.h",
    "content": "#include <Arduino.h>\n\n#define ATOMIC(X) noInterrupts(); X; interrupts();\n\n// want a different avr clock support?\n// TODO: we should do this using macro guards for avrs different clocks freqeuncy setup at compile time\n#define AVR_CLOCK_FREQ\t16000000\n\n// forward declaration of uClockHandler\nvoid uClockHandler();\n\n// AVR ISR Entrypoint\nISR(TIMER1_COMPA_vect)\n{\n    uClockHandler();\n}\n\nvoid initTimer(uint32_t init_clock)\n{\n    ATOMIC(\n        // 16bits Timer1 init\n        // begin at 120bpm (48.0007680122882 Hz)\n        TCCR1A = 0; // set entire TCCR1A register to 0\n        TCCR1B = 0; // same for TCCR1B\n        TCNT1  = 0; // initialize counter value to 0\n        // set compare match register for 48.0007680122882 Hz increments\n        OCR1A = 41665; // = 16000000 / (8 * 48.0007680122882) - 1 (must be <65536)\n        // turn on CTC mode\n        TCCR1B |= (1 << WGM12);\n        // Set CS12, CS11 and CS10 bits for 8 prescaler\n        TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);\n        // enable timer compare interrupt\n        TIMSK1 |= (1 << OCIE1A);\n    )\n}\n\nvoid setTimer(uint32_t us_interval)\n{\n    float tick_hertz_interval = 1/((float)us_interval/1000000);\n\n    uint32_t ocr;\n    uint8_t tccr = 0;\n\n    // 16bits avr timer setup\n    if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1 )) < 65535) {\n        // Set CS12, CS11 and CS10 bits for 1 prescaler\n        tccr |= (0 << CS12) | (0 << CS11) | (1 << CS10);\n    } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 8 )) < 65535) {\n        // Set CS12, CS11 and CS10 bits for 8 prescaler\n        tccr |= (0 << CS12) | (1 << CS11) | (0 << CS10);\n    } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 64 )) < 65535) {\n        // Set CS12, CS11 and CS10 bits for 64 prescaler\n        tccr |= (0 << CS12) | (1 << CS11) | (1 << CS10);\n    } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 256 )) < 65535) {\n        // Set CS12, CS11 and CS10 bits for 256 prescaler\n        tccr |= (1 << CS12) | (0 << CS11) | (0 << CS10);\n    } else if ((ocr = AVR_CLOCK_FREQ / ( tick_hertz_interval * 1024 )) < 65535) {\n        // Set CS12, CS11 and CS10 bits for 1024 prescaler\n        tccr |= (1 << CS12) | (0 << CS11) | (1 << CS10);\n    } else {\n        // tempo not achiavable\n        return;\n    }\n\n    ATOMIC(\n        TCCR1B = 0;\n        OCR1A = ocr-1;\n        TCCR1B |= (1 << WGM12);\n        TCCR1B |= tccr;\n    )\n}\n"
  },
  {
    "path": "src/platforms/esp32-nofrertos.h",
    "content": "#include <Arduino.h>\n\n#define TIMER_ID\t0\n\nhw_timer_t * _uclockTimer = NULL;\nportMUX_TYPE _uclockTimerMux = portMUX_INITIALIZER_UNLOCKED;\n#define ATOMIC(X) portENTER_CRITICAL_ISR(&_uclockTimerMux); X; portEXIT_CRITICAL_ISR(&_uclockTimerMux);\n\n// forward declaration of uClockHandler\nvoid uClockHandler();\n\n// ISR handler\nvoid ARDUINO_ISR_ATTR handlerISR(void)\n{\n    uClockHandler();\n}\n\nvoid initTimer(uint32_t init_clock)\n{\n    _uclockTimer = timerBegin(init_clock);\n\n    // attach to generic uclock ISR\n    timerAttachInterrupt(_uclockTimer, &handlerISR);\n\n    // init clock tick time\n    timerAlarm(_uclockTimer, init_clock, true, 0); \n}\n\nvoid setTimer(uint32_t us_interval)\n{\n    timerAlarmWrite(_uclockTimer, us_interval, true); \n}"
  },
  {
    "path": "src/platforms/esp32.h",
    "content": "#include <Arduino.h>\n#include <freertos/task.h>\n#include <freertos/semphr.h>\n\n// esp32-specific timer\nhw_timer_t * _uclockTimer = NULL;\n\n// FreeRTOS main clock task size in bytes\n#define CLOCK_STACK_SIZE    5*1024 // adjust for your needs, a sequencer with heavy serial handling should be large in size\nTaskHandle_t taskHandle;\n// mutex to protect the shared resource\nSemaphoreHandle_t _mutex;\n// mutex control for task\n#define ATOMIC(X) xSemaphoreTake(_mutex, portMAX_DELAY); X; xSemaphoreGive(_mutex);\n\n// forward declaration of uClockHandler\nvoid uClockHandler();\n\n// ISR handler\nvoid ARDUINO_ISR_ATTR handlerISR(void)\n{\n    BaseType_t xHigherPriorityTaskWoken = pdFALSE;\n    // Send a notification to task1\n    vTaskNotifyGiveFromISR(taskHandle, &xHigherPriorityTaskWoken);\n    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);\n}\n\n// task for user clock process\nvoid clockTask(void *pvParameters)\n{\n    while (1) {\n        // wait for a notification from ISR\n        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);\n        uClockHandler();\n    }\n}\n\nvoid initTimer(uint32_t init_clock)\n{\n    // initialize the mutex for shared resource access\n    _mutex = xSemaphoreCreateMutex();\n\n    // create the clockTask\n    xTaskCreate(clockTask, \"clockTask\", CLOCK_STACK_SIZE, NULL, 1, &taskHandle);\n\n    _uclockTimer = timerBegin(init_clock);\n\n    // attach to generic uclock ISR\n    timerAttachInterrupt(_uclockTimer, &handlerISR);\n\n    // init clock tick time\n    timerAlarm(_uclockTimer, init_clock, true, 0); \n}\n\nvoid setTimer(uint32_t us_interval)\n{\n    timerAlarm(_uclockTimer, us_interval, true, 0); \n}"
  },
  {
    "path": "src/platforms/rp2040.h",
    "content": "#include <Arduino.h>\n#include \"pico/sync.h\"\n\n// RPi-specific timer\nstruct repeating_timer timer;\n\n#define ATOMIC(X) { uint32_t __interrupt_mask = save_and_disable_interrupts(); X; restore_interrupts(__interrupt_mask); }\n\n// forward declaration of uClockHandler\nvoid uClockHandler();\n\n// ISR handler -- called when tick happens\nbool handlerISR(repeating_timer *timer)\n{\n    uClockHandler();\n\n    return true;\n}\n\nvoid initTimer(uint32_t init_clock) {\n    // set up RPi interrupt timer\n    // todo: actually should be -init_clock so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick!\n    add_repeating_timer_us(init_clock, &handlerISR, NULL, &timer);\n}\n\nvoid setTimer(uint32_t us_interval) {\n    cancel_repeating_timer(&timer);\n    // todo: actually should be -us_interval so that timer is set to start init_clock us after last tick, instead of init_clock us after finished processing last tick!\n    add_repeating_timer_us(us_interval, &handlerISR, NULL, &timer);\n}"
  },
  {
    "path": "src/platforms/samd.h",
    "content": "#include <Arduino.h>\n\n// 24 bits timer\n#include <TimerTCC0.h>\n// uses TimerTcc0\n// 16 bits timer\n//#include <TimerTC3.h>\n// uses TimerTc3\n#define ATOMIC(X) noInterrupts(); X; interrupts();\n\n// forward declaration of ISR\nvoid uClockHandler();\n\nvoid initTimer(uint32_t init_clock)\n{\n    TimerTcc0.initialize(init_clock);\n\n    // attach to generic uclock ISR\n    TimerTcc0.attachInterrupt(uClockHandler);\n}\n\nvoid setTimer(uint32_t us_interval)\n{\n    TimerTcc0.setPeriod(us_interval);\n}"
  },
  {
    "path": "src/platforms/software.h",
    "content": "#include <Arduino.h>\n\n/* \n    Generic fallback approach that doesn't rely on any particular MCU's interrupts or RTOS threads etc.\n    Simply checks micros() and compares last time tick happened and interval size to determine when a tick is due.\n    requires calling softwareTimerHandler(micros()); inside loop() in order to trigger tick processing. \n    function signature: void softwareTimerHandler(uint32_t micros_time);\n\n    @author     Doctea\n*/\n\n#define ATOMIC(X) X;\n\n// forward declaration of ISR\nvoid uClockHandler();\n\nuint32_t uclock_last_time_ticked;\nuint32_t uclock_us_interval;\n\n// call this as often as possible to tick the uClock\nvoid softwareTimerHandler(uint32_t micros_time) {\n    if (micros_time - uclock_last_time_ticked >= uclock_us_interval) {\n        uclock_last_time_ticked = micros_time;\n        uClockHandler();\n    }\n}\n\nvoid initTimer(uint32_t init_clock)\n{\n    // basically nothing to do for software-implemented version..?\n    uclock_last_time_ticked = micros();\n}\n\nvoid setTimer(uint32_t us_interval)\n{\n    uclock_us_interval = us_interval;\n}"
  },
  {
    "path": "src/platforms/stm32.h",
    "content": "#include <Arduino.h>\n\n#if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION  < 0x01090000)\n  #error \"Due to API change, this library is compatible with STM32_CORE_VERSION >= 0x01090000. Please update/install stm32duino core.\"\n#endif\n\n#if defined(TIM1)\n  TIM_TypeDef * TimerInstance = TIM1;\n#else\n  TIM_TypeDef * TimerInstance = TIM2;\n#endif\n\n// instantiate HardwareTimer object\nHardwareTimer * _uclockTimer = new HardwareTimer(TimerInstance);\n\n#define ATOMIC(X) noInterrupts(); X; interrupts();\n\n// forward declaration of ISR\nvoid uClockHandler();\n\nvoid initTimer(uint32_t us_interval)\n{\n  _uclockTimer->setOverflow(us_interval, MICROSEC_FORMAT);\n  _uclockTimer->attachInterrupt(uClockHandler);\n  _uclockTimer->resume();\n}\n\nvoid setTimer(uint32_t us_interval)\n{\n  _uclockTimer->setOverflow(us_interval, MICROSEC_FORMAT);\n  _uclockTimer->refresh();\n}\n\n"
  },
  {
    "path": "src/platforms/teensy.h",
    "content": "#include <Arduino.h>\n\n#define ATOMIC(X) noInterrupts(); X; interrupts();\n\nIntervalTimer _uclockTimer;\n\n// forward declaration of ISR\nvoid uClockHandler();\n\nvoid initTimer(uint32_t init_clock)\n{\n    _uclockTimer.begin(uClockHandler, init_clock);\n\n    // Set the interrupt priority level, controlling which other interrupts\n    // this timer is allowed to interrupt. Lower numbers are higher priority,\n    // with 0 the highest and 255 the lowest. Most other interrupts default to 128.\n    // As a general guideline, interrupt routines that run longer should be given\n    // lower priority (higher numerical values).\n    _uclockTimer.priority(80);\n}\n\nvoid setTimer(uint32_t us_interval)\n{\n    _uclockTimer.update(us_interval);\n}\n"
  },
  {
    "path": "src/uClock.cpp",
    "content": "/*!\n *  @file       uClock.cpp\n *  Project     BPM clock generator for Arduino\n *  @brief      A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(RPI2040, Teensy, Seedstudio XIAO M0 and ESP32)\n *  @version    2.3.0\n *  @author     Romulo Silva\n *  @date       10/06/2017\n *  @license    MIT - (c) 2025 - Romulo Silva - contact@midilab.co\n *\n * Permission is hereby granted, free of charge, to any person obtaining a\n * copy of this software and associated documentation files (the \"Software\"),\n * to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense,\n * and/or sell copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE.\n */\n#include \"uClock.h\"\n\n//\n// Compile time selection of Platform implementation of timer setup/control/handler\n//\n#if !defined(USE_UCLOCK_SOFTWARE_TIMER)\n    //\n    // General Arduino AVRs port\n    //\n    #if defined(ARDUINO_ARCH_AVR)\n        #include \"platforms/avr.h\"\n        #define UCLOCK_PLATFORM_FOUND\n    #endif\n    //\n    // Teensyduino ARMs port\n    //\n    #if defined(TEENSYDUINO)\n        #include \"platforms/teensy.h\"\n        #define UCLOCK_PLATFORM_FOUND\n    #endif\n    //\n    // Seedstudio XIAO M0 port\n    //\n    #if defined(SEEED_XIAO_M0)\n        #include \"platforms/samd.h\"\n        #define UCLOCK_PLATFORM_FOUND\n    #endif\n    //\n    // ESP32 family\n    //\n    #if defined(ARDUINO_ARCH_ESP32) || defined(ESP32)\n        #include \"platforms/esp32.h\"\n        #define UCLOCK_PLATFORM_FOUND\n    #endif\n    //\n    // STM32XX family\n    //\n    #if defined(ARDUINO_ARCH_STM32)\n        #include \"platforms/stm32.h\"\n        #define UCLOCK_PLATFORM_FOUND\n    #endif\n    //\n    // RP2040 (Raspberry Pico) family\n    //\n    #if defined(ARDUINO_ARCH_RP2040)\n        #include \"platforms/rp2040.h\"\n        #define UCLOCK_PLATFORM_FOUND\n    #endif\n#endif\n\n//\n// Software Timer for generic, board-agnostic, not-accurate, no-interrupt, software-only port\n// No hardware timer support? fallback to USE_UCLOCK_SOFTWARE_TIMER\n//\n#if !defined(UCLOCK_PLATFORM_FOUND)\n    #pragma message (\"NOTE: uClock is using the 'software timer' approach instead of specific board interrupted support, because board is not supported or because of USE_UCLOCK_SOFTWARE_TIMER build flag. Remember to call uClock.run() inside your loop().\")\n    #include \"platforms/software.h\"\n    #if !defined(USE_UCLOCK_SOFTWARE_TIMER)\n        #define USE_UCLOCK_SOFTWARE_TIMER\n    #endif\n#endif\n\n//\n// Platform specific timer handler/setup/control wrappers\n//\n// global timer counter\nvolatile uint32_t _millis = 0;\n\n// called each tick genarated from platform specific timer\nvoid uClockHandler()\n{\n    _millis = millis();\n    uClock.handleInternalClock();\n}\n\n// initTimer(uint32_t us_interval) and setTimer(uint32_t us_interval)\n// are defined at platform specific code and are platform dependent\nvoid uClockInitTimer()\n{\n    // initialize at 120bpm as default\n    initTimer(uClock.bpmToMicroSeconds(120.00));\n}\n\nvoid uClockSetTimerTempo(float bpm)\n{\n    setTimer(uClock.bpmToMicroSeconds(bpm));\n}\n\nnamespace umodular { namespace clock {\n\nstatic inline uint32_t clock_diff(uint32_t old_clock, uint32_t new_clock)\n{\n    if (new_clock >= old_clock) {\n        return new_clock - old_clock;\n    } else {\n        return new_clock + (4294967295UL - old_clock);\n    }\n}\n\nuClockClass::uClockClass()\n{\n    resetCounters();\n}\n\nuClockClass::~uClockClass()\n{\n    if (sync_callbacks)\n        delete[] sync_callbacks;\n\n    if (ext_interval_buffer)\n        delete[] ext_interval_buffer;\n\n    if (tracks)\n        delete[] tracks;\n}\n\nvoid uClockClass::init()\n{\n    if (ext_interval_buffer == nullptr)\n        setExtIntervalBuffer(1);\n\n    // initialize reference data\n    calculateReferencedata();\n    // initialize hardware timer\n    uClockInitTimer();\n    // first interval calculus\n    setTempo(tempo);\n}\n\nvoid uClockClass::handleInternalClock()\n{\n    static uint32_t counter = 0;\n    static uint32_t sync_interval = 0;\n\n    // for debug usage while developing any application under uClock\n    ++int_overflow_counter;\n\n    if (clock_state <= STARTING) // STOPED=0, PAUSED=1, STARTING=2, SYNCING=3, STARTED=4\n        return;\n\n    // tick phase lock and external tempo match for EXTERNAL_CLOCK mode\n    if (clock_mode == EXTERNAL_CLOCK) {\n        // Tick Phase-lock\n        if (labs(int_clock_tick - ext_clock_tick) > 1) {\n            // only update tick at a full quarter or phase_lock_quarters * a quarter\n            // how many quarters to count until we phase-lock?\n            if ((ext_clock_tick * mod_clock_ref) % (output_ppqn*phase_lock_quarters) == 0) {\n                tick = ext_clock_tick * mod_clock_ref;\n                int_clock_tick = ext_clock_tick;\n                // update any counter reference to lock with int_clock_tick\n                for (uint8_t track=0; track < track_slots_size; track++) {\n                    tracks[track].step_counter = tick/mod_step_ref;\n                    tracks[track].mod_step_counter = 0;\n                }\n                // update counter reference for sync callbacks\n                for (uint8_t i = 0; i < sync_callback_size; i++) {\n                    if (sync_callbacks[i].callback) {\n                        sync_callbacks[i].tick = tick/sync_callbacks[i].sync_ref;\n                        sync_callbacks[i].mod_counter = 0;\n                    }\n                }\n            }\n        }\n\n        // any external interval avaliable to start sync timer?\n        if (ext_interval > 0) {\n            counter = ext_interval;\n            sync_interval = clock_diff(ext_clock_us, micros());\n\n            // phase-multiplier interval\n            if (int_clock_tick <= ext_clock_tick) {\n                counter -= (sync_interval * PHASE_FACTOR) >> 8;\n            } else {\n                if (counter > sync_interval) {\n                    counter += ((counter - sync_interval) * PHASE_FACTOR) >> 8;\n                }\n            }\n\n            external_tempo = constrainBpm(freqToBpm(counter));\n            if (external_tempo != tempo) {\n                tempo = external_tempo;\n                uClockSetTimerTempo(tempo);\n            }\n        }\n    }\n\n    // main input clock counter control\n    if (mod_clock_counter == mod_clock_ref)\n        mod_clock_counter = 0;\n    // process internal clock signal\n    // int_clock_tick is the internal clock reference. mainly used for external clock phase lock\n    if (mod_clock_counter == 0)\n        ++int_clock_tick;\n    ++mod_clock_counter;\n\n    // sync callbacks\n    for (uint8_t i = 0; i < sync_callback_size; i++) {\n        if (sync_callbacks[i].mod_counter == sync_callbacks[i].sync_ref)\n            sync_callbacks[i].mod_counter = 0;\n        if (sync_callbacks[i].mod_counter == 0) {\n            sync_callbacks[i].callback(sync_callbacks[i].tick);\n            // tick sync callback\n            ++sync_callbacks[i].tick;\n        }\n        ++sync_callbacks[i].mod_counter;\n    }\n\n    // StepSeq extension: step callback to support 16th old school style sequencers\n    // with builtin shuffle - process onStepCallback()\n    if (tracks)\n        stepSeqTick();\n\n    // main PPQNCallback\n    if (onOutputPPQNCallback)\n        onOutputPPQNCallback(tick);\n\n    // internal ticking\n    ++tick;\n\n    // for debug usage while developing any application under uClock\n    --int_overflow_counter;\n}\n\nvoid uClockClass::handleExternalClock()\n{\n    static uint32_t now_clock_us = 0;\n    static uint8_t start_sync_counter = 0;\n\n    // for debug usage while developing any application under uClock\n    ++ext_overflow_counter;\n\n    // calculate and store ext_interval\n    now_clock_us = micros();\n    if (ext_clock_us > 0)\n        ext_interval = clock_diff(ext_clock_us, now_clock_us);\n    ext_clock_us = now_clock_us;\n\n    // external clock tick me!\n    ext_clock_tick++;\n\n    switch (clock_state) {\n        case STARTING:\n            clock_state = SYNCING;\n            start_sync_counter = 4;\n            break;\n        case SYNCING:\n            if (--start_sync_counter == 0)\n                clock_state = STARTED;\n            break;\n        default:\n            // accumulate interval incomming ticks data for getTempo() smooth reads on slave clock_mode\n            if (ext_interval > 0) {\n                ext_interval_buffer[ext_interval_idx] = ext_interval;\n                if(++ext_interval_idx >= ext_interval_buffer_size)\n                    ext_interval_idx = 0;\n            }\n            break;\n    }\n\n    // for debug usage while developing any application under uClock\n    --ext_overflow_counter;\n}\n\nvoid uClockClass::clockMe()\n{\n    ATOMIC(handleExternalClock())\n}\n\nvoid uClockClass::start()\n{\n    ATOMIC(resetCounters())\n    start_timer = millis();\n\n    if (clock_mode == INTERNAL_CLOCK) {\n        ATOMIC(clock_state = STARTED)\n    } else {\n        ATOMIC(clock_state = STARTING)\n    }\n\n    if (onClockStartCallback)\n        onClockStartCallback();\n}\n\nvoid uClockClass::stop()\n{\n    ATOMIC(clock_state = STOPED)\n    start_timer = 0;\n    if (onClockStopCallback)\n        onClockStopCallback();\n}\n\nvoid uClockClass::pause()\n{\n    if (clock_state == STARTED) {\n        ATOMIC(clock_state = PAUSED)\n        if (onClockPauseCallback)\n            onClockPauseCallback();\n    } else if (clock_state == PAUSED) {\n        if (clock_mode == INTERNAL_CLOCK) {\n            ATOMIC(clock_state = STARTED)\n        } else if (clock_mode == EXTERNAL_CLOCK) {\n            ATOMIC(clock_state = STARTING)\n        }\n        if (onClockContinueCallback)\n            onClockContinueCallback();\n    }\n}\n\nvoid uClockClass::setClockMode(ClockMode tempo_mode)\n{\n    ATOMIC(\n        clock_mode = tempo_mode;\n        // trying to set external clock while playing?\n        if (clock_mode == EXTERNAL_CLOCK && clock_state == STARTED)\n            clock_state = STARTING;\n    )\n}\n\nuClockClass::ClockMode uClockClass::getClockMode()\n{\n    return clock_mode;\n}\n\n// for software timer implementation(fallback for no timer board support)\nvoid uClockClass::run()\n{\n#if defined(USE_UCLOCK_SOFTWARE_TIMER)\n    // call software timer implementation\n    softwareTimerHandler(micros());\n#endif\n}\n\nvoid uClockClass::stepSeqTick()\n{\n    for (uint8_t track=0; track < track_slots_size; track++) {\n        bool stepProcess = false;\n        if (tracks[track].mod_step_counter == mod_step_ref)\n            tracks[track].mod_step_counter = 0;\n        if (!tracks[track].shuffle.tmplt.active) {\n            if (tracks[track].mod_step_counter == 0)\n                stepProcess = true;\n        } else if (processShuffle(track)) {\n            stepProcess = true;\n        }\n\n        if (stepProcess) {\n            if (onStepGlobalCallback)\n                onStepGlobalCallback(tracks[track].step_counter);\n            if (onStepMultiCallback)\n                onStepMultiCallback(tracks[track].step_counter, track);\n\n            // going forward to the next step call\n            ++tracks[track].step_counter;\n        }\n        ++tracks[track].mod_step_counter;\n    }\n}\n\nvoid uClockClass::setShuffle(bool active, uint8_t track)\n{\n    if (tracks == nullptr)\n        return;\n\n    ATOMIC(tracks[track].shuffle.tmplt.active = active)\n}\n\nbool uClockClass::isShuffled(uint8_t track)\n{\n    if (tracks == nullptr)\n        return false;\n\n    return tracks[track].shuffle.tmplt.active;\n}\n\nvoid uClockClass::setShuffleSize(uint8_t size, uint8_t track)\n{\n    if (tracks == nullptr)\n        return;\n\n    if (size > MAX_SHUFFLE_TEMPLATE_SIZE)\n        size = MAX_SHUFFLE_TEMPLATE_SIZE;\n    ATOMIC(tracks[track].shuffle.tmplt.size = size)\n}\n\nvoid uClockClass::setShuffleData(uint8_t step, int8_t tick, uint8_t track)\n{\n    if (tracks == nullptr)\n        return;\n\n    if (step >= MAX_SHUFFLE_TEMPLATE_SIZE)\n        return;\n    ATOMIC(tracks[track].shuffle.tmplt.step[step] = tick)\n}\n\nvoid uClockClass::setShuffleTemplate(int8_t * shuff, uint8_t size, uint8_t track)\n{\n    if (tracks == nullptr)\n        return;\n\n    //uint8_t size = sizeof(shuff) / sizeof(shuff[0]);\n    if (size > MAX_SHUFFLE_TEMPLATE_SIZE)\n        size = MAX_SHUFFLE_TEMPLATE_SIZE;\n    ATOMIC(tracks[track].shuffle.tmplt.size = size)\n    for (uint8_t i=0; i < size; i++) {\n        setShuffleData(i, shuff[i], track);\n    }\n}\n\nint8_t uClockClass::getShuffleLength(uint8_t track)\n{\n    if (tracks == nullptr)\n        return 0;\n\n    return tracks[track].shuffle.shuffle_length_ctrl;\n}\n\nbool inline uClockClass::processShuffle(uint8_t track)\n{\n    if (tracks == nullptr)\n        return false;\n\n    int8_t mod_shuffle = 0;\n\n    // check shuffle template of current\n    int8_t shff = tracks[track].shuffle.tmplt.step[tracks[track].step_counter%tracks[track].shuffle.tmplt.size];\n\n    if (tracks[track].shuffle.shuffle_shoot_ctrl == false && tracks[track].mod_step_counter == 0)\n        tracks[track].shuffle.shuffle_shoot_ctrl = true;\n\n    if (shff >= 0) {\n        mod_shuffle = tracks[track].mod_step_counter - shff;\n        // any late shuffle? we should skip next tracks[track].mod_step_counter == 0\n        if (tracks[track].shuffle.last_shff < 0 && tracks[track].mod_step_counter != 1) {\n            if (tracks[track].shuffle.shuffle_shoot_ctrl == true)\n                tracks[track].shuffle.shuffle_shoot_ctrl = false;\n            return false;\n        }\n    } else if (shff < 0) {\n        mod_shuffle = tracks[track].mod_step_counter - (mod_step_ref + shff);\n        tracks[track].shuffle.shuffle_shoot_ctrl = true;\n    }\n\n    tracks[track].shuffle.last_shff = shff;\n\n    // shuffle_shoot_ctrl helps keep track if we have shoot or not a note for the step space of output_ppqn/4 pulses\n    if (mod_shuffle == 0 && tracks[track].shuffle.shuffle_shoot_ctrl == true) {\n        // keep track of next note shuffle for current note lenght control\n        tracks[track].shuffle.shuffle_length_ctrl = tracks[track].shuffle.tmplt.step[(tracks[track].step_counter+1)%tracks[track].shuffle.tmplt.size];\n        if (shff > 0)\n            tracks[track].shuffle.shuffle_length_ctrl -= shff;\n        if (shff < 0)\n            tracks[track].shuffle.shuffle_length_ctrl += shff;\n        tracks[track].shuffle.shuffle_shoot_ctrl = false;\n        return true;\n    }\n\n    return false;\n}\n\nuint32_t uClockClass::bpmToMicroSeconds(float bpm)\n{\n    return (60000000.0f / (float)output_ppqn / bpm);\n}\n\nvoid uClockClass::calculateReferencedata()\n{\n    mod_clock_ref = output_ppqn / input_ppqn;\n    mod_step_ref = output_ppqn / 4;\n    // sync callback references update\n    for (uint8_t i = 0; i < sync_callback_size; i++)\n        sync_callbacks[i].sync_ref = output_ppqn / sync_callbacks[i].resolution;\n}\n\nvoid uClockClass::setOutputPPQN(PPQNResolution resolution)\n{\n    // dont allow PPQN lower than PPQN_4 for output clock (to avoid problems with mod_step_ref)\n    if (resolution < PPQN_4)\n        return;\n\n    // dont allow output_ppqn lower than input_ppqn\n    if (resolution < input_ppqn)\n        return;\n\n    ATOMIC(\n        output_ppqn = resolution;\n        calculateReferencedata();\n    )\n}\n\nvoid uClockClass::setInputPPQN(PPQNResolution resolution)\n{\n    // dont allow input_ppqn greater than output_ppqn\n    if (resolution > output_ppqn)\n        return;\n\n    ATOMIC(\n        input_ppqn = resolution;\n        calculateReferencedata();\n    )\n}\n\nvoid uClockClass::setOnSync(PPQNResolution resolution, void (*callback)(uint32_t tick)) {\n    // sets sync callback only if the resolution is lower or equal main clock rate\n    if (resolution > output_ppqn)\n        return;\n\n    // alloc once and forever policy!\n   \t// reallocate by creating a new array, copying data, and deleting the old one\n   \tSyncCallback * new_sync_callbacks = new SyncCallback[sync_callback_size+1];\n   \tif (sync_callbacks != nullptr) {\n  \t\tmemcpy(new_sync_callbacks, sync_callbacks, sizeof(SyncCallback) * (sync_callback_size+1));\n  \t\tdelete[] sync_callbacks;\n   \t}\n    sync_callbacks = new_sync_callbacks;\n\n    sync_callbacks[sync_callback_size].callback = callback;\n    sync_callbacks[sync_callback_size].resolution = resolution;\n\n    ++sync_callback_size;\n}\n\nvoid uClockClass::setTempo(float bpm)\n{\n    if (clock_mode == EXTERNAL_CLOCK)\n        return;\n\n    if (bpm < MIN_BPM || bpm > MAX_BPM)\n        return;\n\n    ATOMIC(tempo = bpm)\n\n    uClockSetTimerTempo(bpm);\n}\n\nfloat uClockClass::getTempo()\n{\n    if (clock_mode == EXTERNAL_CLOCK) {\n        uint64_t acc = 0;\n        uint8_t valid_buffer_size = 0;\n        for (uint8_t i=0; i < ext_interval_buffer_size; i++) {\n            if (ext_interval_buffer[i] > 0) {\n                ATOMIC(acc += ext_interval_buffer[i])\n                ++valid_buffer_size;\n            }\n        }\n        if (acc == 0)\n            return tempo;\n        return constrainBpm(freqToBpm(acc / valid_buffer_size));\n    }\n    return tempo;\n}\n\nfloat inline uClockClass::freqToBpm(uint32_t freq)\n{\n    float usecs = 1/((float)freq/1000000.0);\n    return (float)((float)(usecs/(float)input_ppqn) * 60.0);\n}\n\nfloat inline uClockClass::constrainBpm(float bpm)\n{\n    return (bpm < MIN_BPM) ? MIN_BPM : ( bpm > MAX_BPM ? MAX_BPM : bpm );\n}\n\nvoid uClockClass::setExtIntervalBuffer(size_t buffer_size)\n{\n    if (ext_interval_buffer != nullptr)\n        return;\n\n    // alloc once and forever policy\n    ext_interval_buffer_size = buffer_size;\n    ext_interval_buffer = new uint32_t[ext_interval_buffer_size];\n\n    for (uint8_t i=0; i < ext_interval_buffer_size; i++)\n        ext_interval_buffer[i] = 0;\n}\n\nvoid uClockClass::setPhaseLockQuartersCount(uint8_t count)\n{\n    ATOMIC(phase_lock_quarters = count)\n}\n\nvoid uClockClass::resetCounters()\n{\n    tick = 0;\n    mod_clock_counter = 0;\n    int_clock_tick = 0;\n    ext_clock_tick = 0;\n    ext_clock_us = 0;\n    ext_interval = 0;\n    //ext_interval_idx = 0;\n\n    // sync output counters\n    for (uint8_t i = 0; i < sync_callback_size; i++) {\n        sync_callbacks[i].mod_counter = 0;\n        sync_callbacks[i].tick = 0;\n    }\n\n    // stepseq counters\n    for (uint8_t track=0; track < track_slots_size; track++) {\n        tracks[track].step_counter = 0;\n        tracks[track].mod_step_counter = 0;\n    }\n\n    // external bpm read buffer\n    //for (uint8_t i=0; i < ext_interval_buffer_size; i++)\n    //    ext_interval_buffer[i] = 0;\n}\n\nvoid uClockClass::tap()\n{\n    // we can make use of mod_sync1_ref for tap\n    //uint8_t mod_tap_ref = output_ppqn / PPQN_1;\n    // we only set tap if ClockMode is INTERNAL_CLOCK\n}\n\n// elapsed time support\nuint8_t uClockClass::getNumberOfSeconds(uint32_t time)\n{\n    if ( time == 0 ) {\n        return time;\n    }\n    return ((_millis - time) / 1000) % SECS_PER_MIN;\n}\n\nuint8_t uClockClass::getNumberOfMinutes(uint32_t time)\n{\n    if ( time == 0 ) {\n        return time;\n    }\n    return (((_millis - time) / 1000) / SECS_PER_MIN) % SECS_PER_MIN;\n}\n\nuint8_t uClockClass::getNumberOfHours(uint32_t time)\n{\n    if ( time == 0 ) {\n        return time;\n    }\n    return (((_millis - time) / 1000) % SECS_PER_DAY) / SECS_PER_HOUR;\n}\n\nuint8_t uClockClass::getNumberOfDays(uint32_t time)\n{\n    if ( time == 0 ) {\n        return time;\n    }\n    return ((_millis - time) / 1000) / SECS_PER_DAY;\n}\n\nuint32_t uClockClass::getNowTimer()\n{\n    return _millis;\n}\n\nuint32_t uClockClass::getPlayTime()\n{\n    return start_timer;\n}\n\nuint16_t uClockClass::getIntOverflowCounter()\n{\n    uint16_t counter = 0;\n    ATOMIC(counter = int_overflow_counter)\n    return counter;\n}\n\nuint16_t uClockClass::getExtOverflowCounter()\n{\n    uint16_t counter = 0;\n    ATOMIC(counter = ext_overflow_counter)\n    return counter;\n}\n\n} } // end namespace umodular::clock\n\numodular::clock::uClockClass uClock;\n"
  },
  {
    "path": "src/uClock.h",
    "content": "/*!\n *  @file       uClock.h\n *  Project     BPM clock generator for Arduino\n *  @brief      A Library to implement BPM clock tick calls using hardware interruption. Supported and tested on AVR boards(ATmega168/328, ATmega16u4/32u4 and ATmega2560) and ARM boards(RPI2040, Teensy, Seedstudio XIAO M0 and ESP32)\n *  @version    2.3.0\n *  @author     Romulo Silva\n *  @date       10/06/2017\n *  @license    MIT - (c) 2025 - Romulo Silva - contact@midilab.co\n *\n * Permission is hereby granted, free of charge, to any person obtaining a\n * copy of this software and associated documentation files (the \"Software\"),\n * to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense,\n * and/or sell copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef __U_CLOCK_H__\n#define __U_CLOCK_H__\n\n#include <Arduino.h>\n#include <inttypes.h>\n\nnamespace umodular { namespace clock {\n\n// Shuffle templates are specific for each PPQN output resolution\n// min: -(output_ppqn/4)-1 ticks\n// max: (output_ppqn/4)-1 ticks\n// adjust the size of you template if more than 16 shuffle step info needed\n#define MAX_SHUFFLE_TEMPLATE_SIZE   16\n\n#define MIN_BPM\t1\n#define MAX_BPM\t500\n\n#define PHASE_FACTOR 16\n\n#define MICROS_PER_MIN (60000000UL)\n#define SECS_PER_MIN  (60UL)\n#define SECS_PER_HOUR (3600UL)\n#define SECS_PER_DAY  (SECS_PER_HOUR * 24L)\n\nclass uClockClass {\n\n    public:\n        enum ClockMode {\n            INTERNAL_CLOCK = 0,\n            EXTERNAL_CLOCK\n        };\n\n        enum ClockState {\n            STOPED = 0,\n            PAUSED,\n            STARTING,\n            SYNCING,\n            STARTED\n        };\n\n        enum PPQNResolution {\n            PPQN_1 = 1,\n            PPQN_2 = 2,\n            PPQN_4 = 4,\n            PPQN_8 = 8,\n            PPQN_12 = 12,\n            PPQN_24 = 24,\n            PPQN_48 = 48,\n            PPQN_96 = 96,\n            PPQN_384 = 384,\n            PPQN_480 = 480,\n            PPQN_960 = 960\n        };\n\n        ClockState clock_state = STOPED;\n\n        uClockClass();\n        ~uClockClass();\n\n        // set main input and output clock rates\n        void setOutputPPQN(PPQNResolution resolution);\n        void setInputPPQN(PPQNResolution resolution);\n        \n        // callbacks setup\n        void setOnOutputPPQN(void (*callback)(uint32_t tick)) {\n            onOutputPPQNCallback = callback;\n        }\n\n        // multiple output sync clock signatures callback register\n        void setOnSync(PPQNResolution resolution, void (*callback)(uint32_t tick));\n\n        void setOnClockStart(void (*callback)()) {\n            onClockStartCallback = callback;\n        }\n\n        void setOnClockStop(void (*callback)()) {\n            onClockStopCallback = callback;\n        }\n\n        void setOnClockPause(void (*callback)()) {\n            onClockPauseCallback = callback;\n        }\n\n        void setOnClockContinue(void (*callback)()) {\n            onClockContinueCallback = callback;\n        }\n\n        // Step Seq extension support?\n        // Step Seq supports per track control:\n        // shuffle: YES\n        // shift: ROADMAP\n        // direction: ROADMAP\n        // keep API compatibility for setOnStep global track\n        void setOnStep(void (*callback)(uint32_t step)) {\n            if (tracks == nullptr) {\n                track_slots_size = 1;\n                // alloc once and forever policy\n                tracks = new TRACK_SLOT[track_slots_size];\n                onStepGlobalCallback = callback;\n            }\n        }\n        // extended stepSeq multitrack support for setOnStep\n        void setOnStep(void (*callback)(uint32_t step, uint8_t track), uint8_t track_number) {\n            if (tracks == nullptr) {\n                track_slots_size = track_number;\n                // alloc once and forever policy\n                tracks = new TRACK_SLOT[track_slots_size];\n                onStepMultiCallback = callback;\n            }\n        }\n\n        void init();\n\n        // the main processing of internal and external tick system\n        void handleInternalClock();\n        void handleExternalClock();\n\n        // external class control\n        void start();\n        void stop();\n        void pause();\n        void setTempo(float bpm);\n        float getTempo();\n\n        // Step Seq extension for global and multi track sequences control\n        // void setShift(int8_t shift, uint8_t track = 0);\n        // void setDirection(uint8_t direction, uint8_t track = 0);\n        void setShuffle(bool active, uint8_t track = 0);\n        bool isShuffled(uint8_t track = 0);\n        void setShuffleSize(uint8_t size, uint8_t track = 0);\n        void setShuffleData(uint8_t step, int8_t tick, uint8_t track = 0);\n        void setShuffleTemplate(int8_t * shuff, uint8_t size, uint8_t track = 0);\n        // use this to know how many positive or negative ticks to add to current note length\n        int8_t getShuffleLength(uint8_t track = 0);\n\n        // for software timer implementation(fallback for no board support)\n        void run();\n\n        // external timming control\n        void setClockMode(ClockMode tempo_mode);\n        ClockMode getClockMode();\n        void clockMe();\n        void setPhaseLockQuartersCount(uint8_t count);\n        // for smooth slave tempo calculate display you should raise the\n        // buffer_size of ext_interval_buffer in between 64 to 128. 254 max size.\n        // note: this doesn't impact on sync time, only display time getTempo()\n        // if you dont want to use it, it is default set it to 1 for memory save\n        void setExtIntervalBuffer(size_t buffer_size);\n\n        // todo!\n        void tap();\n\n        // elapsed time support\n        uint8_t getNumberOfSeconds(uint32_t time);\n        uint8_t getNumberOfMinutes(uint32_t time);\n        uint8_t getNumberOfHours(uint32_t time);\n        uint8_t getNumberOfDays(uint32_t time);\n        uint32_t getNowTimer();\n        uint32_t getPlayTime();\n\n        // interrupt overflow debug\n        uint16_t getIntOverflowCounter();\n        uint16_t getExtOverflowCounter();\n\n        uint32_t bpmToMicroSeconds(float bpm);\n        \n        void resetCounters();\n\n    private:\n        float inline freqToBpm(uint32_t freq);\n        float inline constrainBpm(float bpm);\n        void calculateReferencedata();\n\n        // callbacks\n        void (*onOutputPPQNCallback)(uint32_t tick) = nullptr;\n        void (*onClockStartCallback)() = nullptr;\n        void (*onClockStopCallback)() = nullptr;\n        void (*onClockPauseCallback)() = nullptr;\n        void (*onClockContinueCallback)() = nullptr;\n        // step seq extension for global and multi track sequences control\n        void (*onStepGlobalCallback)(uint32_t step) = nullptr;\n        void (*onStepMultiCallback)(uint32_t step, uint8_t track) = nullptr;\n        \n\n        typedef struct {\n            bool active = false;\n            uint8_t size = MAX_SHUFFLE_TEMPLATE_SIZE;\n            int8_t step[MAX_SHUFFLE_TEMPLATE_SIZE] = {0}; // int8 supports max PPQN_480 of internal clock resolution\n        } SHUFFLE_TEMPLATE;\n        \n        typedef struct {\n            volatile SHUFFLE_TEMPLATE tmplt;\n            int8_t last_shff = 0; // int8 supports max PPQN_480 of internal clock resolution\n            bool shuffle_shoot_ctrl = true;\n            volatile int8_t shuffle_length_ctrl = 0;\n        } SHUFFLE_DATA;\n        \n        typedef struct {\n            SHUFFLE_DATA shuffle;\n            //int8_t shift = 0;\n            //uint8_t direction = 0;\n            uint32_t step_counter = 0;\n            uint8_t mod_step_counter;\n        } TRACK_SLOT;\n        \n        // sync callback structure for dynamic multiple sync outputs support\n        struct SyncCallback {\n            void (*callback)(uint32_t tick) = nullptr;\n            uint8_t mod_counter = 0;\n            uint16_t sync_ref = 0;\n            uint32_t tick = 0;\n            PPQNResolution resolution;\n        };\n        \n        // sync callback data\n        SyncCallback * sync_callbacks = nullptr;\n        uint8_t sync_callback_size = 0;\n\n        // clock core\n        // input/output tick resolution\n        PPQNResolution output_ppqn = PPQN_96;\n        PPQNResolution input_ppqn = PPQN_24;\n        volatile float tempo = 120.0;\n        volatile ClockMode clock_mode = INTERNAL_CLOCK;\n        uint32_t start_timer = 0;\n\n        // output and internal counters, ticks and references\n        volatile uint32_t tick = 0;\n        volatile uint32_t int_clock_tick = 0;\n        uint8_t mod_step_ref = 0;\n        uint8_t mod_clock_counter = 0;\n        uint16_t mod_clock_ref = 0;\n\n        // external clock control\n        volatile uint32_t ext_clock_us = 0;\n        volatile uint32_t ext_clock_tick = 0;\n        volatile uint32_t ext_interval = 0;\n        volatile float external_tempo = tempo;\n        uint8_t phase_lock_quarters = 1;\n\n        // debug interrupts overflow\n        volatile uint16_t int_overflow_counter = 0;\n        volatile uint16_t ext_overflow_counter = 0;\n\n        // StepSeq extension\n        // main stepseq tick processor\n        void stepSeqTick();\n        // stepseq shuffle processor\n        bool inline processShuffle(uint8_t track = 0);\n        TRACK_SLOT * tracks = nullptr;\n        size_t track_slots_size = 0;\n\n        // external clock bpm read calculus getTempo()\n        volatile uint32_t * ext_interval_buffer = nullptr;\n        size_t ext_interval_buffer_size = 0;\n        uint16_t ext_interval_idx = 0;\n};\n\n} } // end namespace umodular::clock\n\nextern umodular::clock::uClockClass uClock;\n\nextern \"C\" {\n    extern volatile uint32_t _millis;\n}\n\n#endif /* __U_CLOCK_H__ */\n"
  }
]