{"id":47055956,"url":"https://github.com/chronick/duopulse","last_synced_at":"2026-03-12T03:10:25.677Z","repository":{"id":332485125,"uuid":"1133930755","full_name":"chronick/duopulse","owner":"chronick","description":"A two-channel eurorack sequencer for Daisy SP and Patch.init()","archived":false,"fork":false,"pushed_at":"2026-01-22T16:43:44.000Z","size":3624,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-22T16:53:40.309Z","etag":null,"topics":["daisysp","drum-sequencer","eurorack","patch-init","sequencer"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chronick.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-14T02:28:41.000Z","updated_at":"2026-01-22T14:47:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/chronick/duopulse","commit_stats":null,"previous_names":["chronick/duopulse"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chronick/duopulse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronick%2Fduopulse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronick%2Fduopulse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronick%2Fduopulse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronick%2Fduopulse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chronick","download_url":"https://codeload.github.com/chronick/duopulse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronick%2Fduopulse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30413694,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-12T00:40:14.898Z","status":"online","status_checked_at":"2026-03-12T02:00:07.260Z","response_time":114,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["daisysp","drum-sequencer","eurorack","patch-init","sequencer"],"created_at":"2026-03-12T03:10:24.986Z","updated_at":"2026-03-12T03:10:25.669Z","avatar_url":"https://github.com/chronick.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DaisySP Patch.Init Eurorack Firmware\n\nCustom firmware for the Patch.Init Eurorack module using DaisySP DSP library.\n\n## Overview\n\nThis project provides a custom firmware implementation for the Electro-Smith Patch.Init Eurorack module. The Patch.Init is a versatile eurorack module featuring:\n\n- 2x CV inputs (±5V)\n- 2x CV outputs (±5V)\n- 2x audio inputs\n- 2x audio outputs\n- Multiple GPIO pins for buttons, LEDs, and encoders\n- STM32H7 microcontroller with floating-point unit\n\n## DuoPulse v5: 2-Voice Algorithmic Drum Sequencer\n\nDuoPulse v5 is an opinionated 2-voice percussion sequencer designed for electronic music from club-ready techno to experimental IDM. Version 5 features **zero shift layers** - every parameter is directly accessible through 8 knobs across two modes.\n\n### Core Design Philosophy\n\n1. **Zero shift layers**: Every parameter directly accessible\n2. **CV law**: CV1-4 always modulate performance parameters regardless of mode\n3. **Knob pairing**: Related functions across Performance/Config modes\n4. **Deterministic variation**: Same settings + seed = identical output\n\n### Control Layout\n\n#### Performance Mode (Switch UP)\n\n| Knob | Parameter | 0% | 100% |\n|------|-----------|-----|------|\n| K1 | **ENERGY** | Sparse | Busy |\n| K2 | **SHAPE** | Stable (euclidean) | Wild (weighted random) |\n| K3 | **AXIS X** | Grounded (downbeats) | Floating (offbeats) |\n| K4 | **AXIS Y** | Simple | Complex |\n\n#### Config Mode (Switch DOWN)\n\n| Knob | Parameter | 0% | 100% |\n|------|-----------|-----|------|\n| K1 | **CLOCK DIV** | ÷4 (slow) | ×4 (fast) |\n| K2 | **SWING** | Straight | Heavy swing |\n| K3 | **DRIFT** | Locked (same each phrase) | Evolving |\n| K4 | **ACCENT** | Flat (all hits equal) | Dynamic (ghosts to accents) |\n\n### Key Features\n\n**SHAPE Zones**: Three-way pattern character blending\n- 0-30% STABLE: Humanized euclidean, techno, four-on-floor\n- 30-70% SYNCOPATED: Funk, displaced, tension\n- 70-100% WILD: IDM, chaos, weighted random\n\n**Voice Relationship (COMPLEMENT)**: Voice 2 (shimmer) fills gaps in Voice 1 (anchor) pattern. DRIFT controls placement variation.\n\n**ACCENT Velocity**: Position-aware dynamics from ghost notes to accents based on metric weight.\n\n**AUX Output**: Fill Gate (default) or secret Hat Burst mode via Hold+Switch gesture.\n\n### CV Inputs (Always Modulate Performance Params)\n\n| CV | Modulates | Range |\n|----|-----------|-------|\n| CV 1 | ENERGY | ±50% |\n| CV 2 | SHAPE | ±50% |\n| CV 3 | AXIS X | ±50% |\n| CV 4 | AXIS Y | ±50% |\n\n### Power-On Behavior\n\n**Fresh-start boot**: All settings reset to musical defaults\n- Performance knobs read from hardware immediately\n- ENERGY=50%, SHAPE=30%, AXIS X/Y=50%\n- CLOCK DIV=×1, SWING=50%, DRIFT=0%, ACCENT=50%\n- **Nothing persists** across power cycles\n\nSee [docs/specs/](docs/specs/) for the complete specification.\n\n## Prerequisites\n\n- **Hardware**: Electro-Smith Patch.Init Eurorack module\n- **Software**:\n  - ARM GCC toolchain (for STM32)\n  - `arm-none-eabi-gcc` compiler\n  - `make` build system\n  - `dfu-util` for firmware deployment (or STM32CubeProgrammer)\n  - **For testing**: Catch2 testing framework (optional)\n- **DaisySP**: Included as a git submodule\n\n### Installing Dependencies\n\n**macOS** (using Homebrew):\n```bash\nbrew install arm-none-eabi-gcc dfu-util\n```\n\n**Linux** (Debian/Ubuntu):\n```bash\nsudo apt-get update\nsudo apt-get install gcc-arm-none-eabi dfu-util make\n```\n\n**Catch2** (for unit testing):\n```bash\n# Option 1: Install system-wide (recommended)\ngit clone https://github.com/catchorg/Catch2.git\ncd Catch2\ncmake -Bbuild -H. -DBUILD_TESTING=OFF\nsudo cmake --build build/ --target install\n\n# Option 2: Use package manager\n# macOS: brew install catch2\n# Linux: sudo apt-get install catch2 (if available)\n```\n\n## Project Structure\n\n```\n.\n├── DaisySP/              # DaisySP library (git submodule)\n├── src/                  # Source files\n│   ├── main.cpp         # Main firmware entry point\n│   └── ...\n├── inc/                  # Header files\n│   └── ...\n├── tests/                # Unit tests\n│   ├── test_main.cpp    # Test runner\n│   └── ...\n├── Makefile             # Build configuration\n├── setup.sh             # Setup script for initial configuration\n├── .cursor/             # Cursor IDE rules\n│   └── rules\n└── README.md            # This file\n```\n\n## Build Instructions\n\n### Initial Setup\n\n**Quick Setup** (recommended):\n```bash\n# Clone the repository\ngit clone --recursive https://github.com/yourusername/duopulse.git\ncd duopulse\n\n# Run setup script (initializes submodules and builds DaisySP)\n./setup.sh\n\n# Build the firmware\nmake\n```\n\n**Manual Setup**:\n1. **Clone the repository** (including submodules):\n   ```bash\n   git clone --recursive https://github.com/yourusername/duopulse.git\n   cd duopulse\n   ```\n\n   If you've already cloned without submodules:\n   ```bash\n   git submodule update --init --recursive\n   ```\n\n2. **Build DaisySP library**:\n   ```bash\n   cd DaisySP\n   make\n   cd ..\n   ```\n\n3. **Build the firmware**:\n   ```bash\n   make\n   ```\n\n### Build Targets\n\n- `make` or `make all` - Build the firmware\n- `make clean` - Remove build artifacts\n- `make rebuild` - Clean and rebuild\n- `make test` - Build and run unit tests\n- `make program` - Flash firmware to Patch.Init module (requires DFU mode)\n- `make daisy-update` - Update DaisySP submodule to latest version\n\n### Build Configuration\n\nThe Makefile supports the following variables:\n\n- `DAISYSP_PATH` - Path to DaisySP (default: `./DaisySP`)\n- `BUILD_DIR` - Build output directory (default: `build`)\n- `TARGET` - Target board (default: `patch`)\n- `DEBUG` - Enable debug symbols (set `DEBUG=1`)\n\nExample:\n```bash\nmake DEBUG=1 BUILD_DIR=debug_build\n```\n\n## Test Instructions\n\n### Running Unit Tests\n\nThe project includes a unit test framework using Catch2:\n\n1. **Run all tests**:\n   ```bash\n   make test\n   ```\n\n2. **Run specific test**:\n   ```bash\n   ./build/test_runner [test_name]\n   ```\n\n3. **Run tests with verbose output**:\n   ```bash\n   ./build/test_runner --success\n   ```\n\n### Writing Tests\n\nTests are located in the `tests/` directory. Each test file should:\n\n- Include the Catch2 header: `#include \u003ccatch2/catch.hpp\u003e`\n- Use `TEST_CASE()` macro for test cases\n- Use `REQUIRE()` or `CHECK()` for assertions\n\nExample:\n```cpp\n#include \u003ccatch2/catch.hpp\u003e\n#include \"../src/my_module.h\"\n\nTEST_CASE(\"MyModule processes audio correctly\")\n{\n    MyModule module;\n    module.Init(48000.0f);\n    \n    float input = 0.5f;\n    float output = module.Process(input);\n    \n    REQUIRE(output \u003e= -1.0f);\n    REQUIRE(output \u003c= 1.0f);\n}\n```\n\n### Test Coverage\n\nTo generate test coverage reports:\n\n```bash\nmake test-coverage\n```\n\nThis requires `gcov` and `lcov` to be installed.\n\n## Deploy Instructions\n\n### Preparing the Module\n\n1. **Enter DFU (Device Firmware Update) mode**:\n   - Power off the module\n   - Hold the BOOT button (if available) or use the DFU jumper\n   - Power on the module while holding BOOT\n   - Release BOOT button\n   - The module should now be in DFU mode\n\n2. **Verify DFU mode**:\n   ```bash\n   dfu-util --list\n   ```\n   You should see the STM32 device listed.\n\n### Flashing Firmware\n\n**Method 1: Using Makefile** (recommended)\n```bash\nmake program\n```\n\n**Method 2: Using dfu-util directly**\n```bash\ndfu-util -a 0 -s 0x08000000:leave -D build/patch-init-firmware.bin\n```\n\n**Method 3: Using STM32CubeProgrammer**\n1. Open STM32CubeProgrammer\n2. Select \"USB\" connection\n3. Connect to the device\n4. Load the `.bin` or `.hex` file from `build/`\n5. Click \"Download\"\n\n### Post-Flash\n\nAfter flashing:\n1. Power cycle the module (or use `:leave` flag with dfu-util)\n2. The module should boot with the new firmware\n3. Test audio and CV I/O to verify functionality\n\n### Troubleshooting\n\n- **Device not found**: Ensure module is in DFU mode and USB cable is connected\n- **Permission denied**: Add udev rules for STM32 DFU devices (Linux) or run with sudo\n- **Flash fails**: Verify module is in DFU mode and try resetting the module\n\n## Upgrading DaisySP\n\n### Update to Latest Version\n\n1. **Navigate to DaisySP directory**:\n   ```bash\n   cd DaisySP\n   ```\n\n2. **Fetch and checkout latest version**:\n   ```bash\n   git fetch origin\n   git checkout master  # or specific tag/commit\n   git pull origin master\n   ```\n\n3. **Rebuild DaisySP**:\n   ```bash\n   make clean\n   make\n   ```\n\n4. **Return to project root and rebuild**:\n   ```bash\n   cd ..\n   make clean\n   make\n   ```\n\n### Update to Specific Version/Tag\n\n```bash\ncd DaisySP\ngit fetch --tags\ngit checkout v1.0.0  # Replace with desired version\nmake clean\nmake\ncd ..\nmake clean\nmake\n```\n\n### Update Submodule Reference\n\nAfter updating DaisySP, commit the submodule reference:\n\n```bash\ngit add DaisySP\ngit commit -m \"Update DaisySP to version X.X.X\"\n```\n\n### Checking Current Version\n\n```bash\ncd DaisySP\ngit describe --tags\ncd ..\n```\n\n## Development Workflow\n\n1. **Create a feature branch**:\n   ```bash\n   git checkout -b feature/my-feature\n   ```\n\n2. **Make changes and test**:\n   ```bash\n   make clean\n   make\n   make test\n   ```\n\n3. **Flash and test on hardware**:\n   ```bash\n   make program\n   ```\n\n4. **Commit changes**:\n   ```bash\n   git add .\n   git commit -m \"Description of changes\"\n   ```\n\n5. **Push and create pull request**\n\n## Hardware Configuration\n\n### Patch.Init Pin Assignments\n\nRefer to the Patch.Init documentation for specific pin assignments. Common configurations:\n\n- **CV Inputs**: ADC channels 0-1\n- **CV Outputs**: DAC channels 0-1\n- **Audio Inputs**: I2S/SAI interface\n- **Audio Outputs**: I2S/SAI interface\n- **GPIO**: Configurable via `daisy::GPIO`\n\n### Sample Rate\n\nDefault sample rate: **48kHz**\n\nTo change:\n```cpp\npatch.SetAudioSampleRate(daisy::SaiHandle::Config::SampleRate::SAI_96KHZ);\n```\n\n### Block Size\n\nDefault block size: **4 samples**\n\nTo change:\n```cpp\npatch.SetAudioBlockSize(8);\n```\n\n## Contributing\n\n1. Follow the coding standards in `.cursor/rules`\n2. Write unit tests for new features\n3. Update documentation as needed\n4. Test on hardware before submitting PR\n\n## License\n\n[Specify your license here]\n\n## Resources\n\n- [DaisySP Documentation](https://github.com/electro-smith/DaisySP)\n- [Daisy Hardware Documentation](https://github.com/electro-smith/DaisyWiki)\n- [Patch.Init Module Info](https://www.electro-smith.com/daisy/patch)\n- [STM32 Documentation](https://www.st.com/en/microcontrollers-microprocessors/stm32h7-series.html)\n\n## Troubleshooting\n\n### Build Issues\n\n- **Missing toolchain**: Install ARM GCC toolchain\n- **DaisySP not found**: Run `git submodule update --init --recursive`\n- **Linker errors**: Ensure DaisySP is built (`cd DaisySP \u0026\u0026 make`)\n\n### Runtime Issues\n\n- **No audio output**: Check audio callback registration and hardware connections\n- **CV not working**: Verify ADC/DAC configuration and pin assignments\n- **Module crashes**: Check for stack overflow, uninitialized variables, or division by zero\n\n### Debugging\n\nEnable debug output:\n```bash\nmake DEBUG=1\n```\n\nUse a debugger (OpenOCD + GDB) for hardware debugging:\n```bash\nmake debug\n```\n\n#### Runtime Logging\n\nThe firmware includes a USB serial logging system for debugging without a hardware debugger.\n\n**Viewing Log Output**:\n```bash\n# macOS/Linux - using screen\nscreen /dev/tty.usbmodem* 115200\n\n# Alternative - using make target (saves to /tmp)\nmake listen\n\n# Exit screen: Ctrl-A then \\ then y\n```\n\n**Log Levels**:\n- `TRACE`: Verbose debugging\n- `DEBUG`: Development info (mode changes, events)\n- `INFO`: Normal operation (boot, config)\n- `WARN`: Warnings\n- `ERROR`: Critical issues\n\n**Configuration**:\n\nAdjust log levels in `Makefile`:\n```makefile\n# Development: DEBUG+ logs, default INFO\nCXXFLAGS += -DLOG_COMPILETIME_LEVEL=1\nCXXFLAGS += -DLOG_DEFAULT_LEVEL=2\n\n# Release: WARN/ERROR only\nCXXFLAGS += -DLOG_COMPILETIME_LEVEL=3\nCXXFLAGS += -DLOG_DEFAULT_LEVEL=3\n```\n\nSee `CLAUDE.md` for complete logging documentation.\n\n## Support\n\nFor issues and questions:\n- Open an issue on GitHub\n- Check the DaisySP documentation\n- Visit the [Daisy Forum](https://forum.electro-smith.com/)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchronick%2Fduopulse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchronick%2Fduopulse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchronick%2Fduopulse/lists"}