From b66ccbade91a9b4791c02a64e2630809a32d819e Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:03:51 +0200 Subject: [PATCH 01/12] Update and consolidate project documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: rewrite for macOS/Linux desktop (drop mobile), add usage snippet, fix CI badge (Travis → GitHub Actions), link docs - CONTRIBUTING.md: new, covers build/test and PR workflow - docs/architecture.md: new, Mermaid audio routing diagram - docs/osc-api.md: moved from doc/OSC API.md - examples/README.md: new, describes thADDeus and sampler - CLAUDE.md: add domain/API reference pointers - doc/Notes.md, doc/diagrams/: removed (stale) --- CLAUDE.md | 74 +++++- CONTRIBUTING.md | 22 ++ README.md | 95 ++++--- doc/Notes.md | 44 --- doc/diagrams/inputs_outputs_buses.svg | 369 -------------------------- docs/architecture.md | 36 +++ doc/OSC API.md => docs/osc-api.md | 0 examples/README.md | 13 + 8 files changed, 205 insertions(+), 448 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 doc/Notes.md delete mode 100644 doc/diagrams/inputs_outputs_buses.svg create mode 100644 docs/architecture.md rename doc/OSC API.md => docs/osc-api.md (100%) create mode 100644 examples/README.md diff --git a/CLAUDE.md b/CLAUDE.md index 5edb1e2e..2779616b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,67 @@ +# CLAUDE.md + +## Fundamentals + +### 1. Think Before Coding + +**Don't assume. Don't hide confusion. Surface tradeoffs.** + +Before implementing: +- State your assumptions explicitly. If uncertain, ask. +- If multiple interpretations exist, present them - don't pick silently. +- If a simpler approach exists, say so. Push back when warranted. +- If something is unclear, stop. Name what's confusing. Ask. + +### 2. Simplicity First + +**Minimum code that solves the problem. Nothing speculative.** + +- No features beyond what was asked. +- No abstractions for single-use code. +- No "flexibility" or "configurability" that wasn't requested. +- No error handling for impossible scenarios. +- If you write 200 lines and it could be 50, rewrite it. + +Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. + +### 3. Surgical Changes + +**Touch only what you must. Clean up only your own mess.** + +When editing existing code: +- Don't "improve" adjacent code, comments, or formatting. +- Don't refactor things that aren't broken. +- Match existing style, even if you'd do it differently. +- If you notice unrelated dead code, mention it - don't delete it. + +When your changes create orphans: +- Remove imports/variables/functions that YOUR changes made unused. +- Don't remove pre-existing dead code unless asked. + +The test: Every changed line should trace directly to the user's request. + +### 4. Goal-Driven Execution + +**Define success criteria. Loop until verified.** + +Transform tasks into verifiable goals: +- "Add validation" → "Write tests for invalid inputs, then make them pass" +- "Fix the bug" → "Write a test that reproduces it, then make it pass" +- "Refactor X" → "Ensure tests pass before and after" + +For multi-step tasks, state a brief plan: +``` +1. [Step] → verify: [check] +2. [Step] → verify: [check] +3. [Step] → verify: [check] +``` + +Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. + +--- + +**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes. + ## Agent skills ### Issue tracker @@ -12,7 +76,7 @@ Uses the default five-role vocabulary (needs-triage, needs-info, ready-for-agent Single-context layout: one `CONTEXT.md` + `docs/adr/` at the repo root. See `docs/agents/domain.md`. -## GitHub workflows +## Workflows - Create PRs for non-trivial changes. This allows reviewing code and running actions before merging. - Create PR branches before submitting a PR or when prompted to submit changes to a PR branch. @@ -20,3 +84,11 @@ Single-context layout: one `CONTEXT.md` + `docs/adr/` at the repo root. See `doc - Before committing changes or submitting PRs, build locally and run tests. - When merging PRs via gh, update the working copy accordingly (pull and delete PR branch). - Keep PR branch name descriptive but short. No prefixes like `feat/`. Dashes: `my-awesome-new-feature`. +- See `CONTRIBUTING.md` for the human-facing version of these workflows. + +## Domain and API references + +- Use the domain language in `CONTEXT.md`; avoid the listed synonyms. +- Before changing build structure or dependencies, check `docs/adr/` for prior decisions. +- When working with synth, node, or bus commands, consult `docs/osc-api.md`. +- For audio routing and node tree structure, see `docs/architecture.md`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..71097625 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# Contributing + +## Build and test + +```sh +cmake --preset debug +cmake --build build/debug +ctest --test-dir build/debug --output-on-failure +``` + +Always build and run tests before submitting changes. + +## Workflow + +- **Non-trivial changes**: open a pull request. This lets CI run and allows review before merging. +- **Small, low-risk changes**: commit directly to `develop`. +- **Branch names**: short, descriptive, dashes. No prefixes. Example: `fix-rtaudio-latency`. +- **Merging**: use squash or merge commit via GitHub. Delete the branch after merging. + +## Code style + +Match the style of the surrounding code. C++17, C99. No new dependencies without discussion. diff --git a/README.md b/README.md index 9bf7a033..bbaec966 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,85 @@ -[![Build Status](https://travis-ci.org/samplecount/methcla.svg?branch=develop)](https://travis-ci.org/samplecount/methcla) +[![CI](https://github.com/samplecount/methcla/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/samplecount/methcla/actions/workflows/ci.yml) -# Methcla - Mobile sound engine +# Methcla -Methcla is a light-weight, efficient sound engine for mobile devices, see our [website](http://methc.la) for the full picture. +A real-time C++ audio engine library for macOS and Linux. Exposes a C API with C++ bindings for embedding in host applications. -## Build requirements +## Requirements -* GCC >= 4.7 or Clang >= 3.3 +- CMake 3.24+, C++17 (GCC or Clang) +- Optional: [RtAudio](https://www.music.mcgill.ca/~gary/rtaudio/) for audio I/O (`-DMETHCLA_ENABLE_RTAUDIO=ON`) +- Optional: [libsndfile](https://libsndfile.github.io/libsndfile/) for soundfile support on Linux -## Building the sound engine +## Build -Our build system is written in Haskell using the [Shake](https://github.com/ndmitchell/shake) library and you need to install the latest [Haskell platform](http://www.haskell.org/platform/) before building Methcla. +```sh +cmake --preset debug # configure (or: release) +cmake --build build/debug # build +ctest --test-dir build/debug --output-on-failure # test +``` -First off, don't forget to +## Usage - git submodule update --init --recursive +```cpp +#include +#include -after pulling from Methcla's repository. +Methcla::EngineOptions options; +options.addLibrary(methcla_plugins_sine); -To build a specific target (see below for a list of possible targets): +Methcla::Engine engine(options); +engine.start(); - ./shake TARGET +// Create a sine synth (freq=440 Hz, amp=0.5) on hardware output bus 0 +Methcla::Request request(&engine); +request.openBundle(); +auto synth = request.synth(METHCLA_PLUGINS_SINE_URI, engine.root(), {440.f, 0.5f}); +request.activate(synth); +request.mapOutput(synth, 0, Methcla::AudioBusId(0), Methcla::kBusMappingExternal); +request.closeBundle(); +request.send(); -To clean everything +// Free the synth when done +Methcla::Request stop(&engine); +stop.openBundle(); +stop.free(synth); +stop.closeBundle(); +stop.send(); +``` - ./shake clean +See [`docs/osc-api.md`](docs/osc-api.md) for the full command protocol and [`docs/architecture.md`](docs/architecture.md) for an overview of audio routing. -Use the `-j` flag for parallel builds: +## Integration - ./shake -j4 test +**add_subdirectory:** -The build script automatically passes the number of core in your build machine, use `-j1` to force a sequential build. +```cmake +add_subdirectory(path/to/methcla) +target_link_libraries(myapp PRIVATE methcla::methcla) +``` -The `-c` flag allows to select a build configuration (*release* or *debug*): +**FetchContent:** - ./shake -c release test +```cmake +include(FetchContent) +FetchContent_Declare(methcla + GIT_REPOSITORY https://github.com/samplecount/methcla.git + GIT_TAG develop) +FetchContent_MakeAvailable(methcla) +target_link_libraries(myapp PRIVATE methcla::methcla) +``` -Display the list of Shake command line options: +**find_package** (after `cmake --install`): - ./shake -h - -All built files are put in the `build` directory. - -Here's a non-exhaustive list of targets: - -* `test`: Run the test suite; this should be first thing to do when porting to a new platform. -* `desktop`: Build a shared library for the host operating system -* `iphoneos`: Build a static library for iOS devices -* `iphone-universal`: Build a static library for iOS devices and simulator -* `android`: Build a static library for Android -* `pnacl`: Build a static library for Pepper/PNaCl +```cmake +find_package(methcla REQUIRED) +target_link_libraries(myapp PRIVATE methcla::methcla) +``` ## Examples -[thADDeus](https://github.com/samplecount/methcla/tree/develop/engine/examples/thADDeus) is an example project that builds the engine for iOS and Android devices and provides a simple multitouch sine synthesizer. +See [`examples/`](examples/) for sample applications. + +## Contributing -The [sampler](https://github.com/samplecount/methcla/tree/develop/engine/examples/sampler) example is a simple multitouch sampler application. +See [`CONTRIBUTING.md`](CONTRIBUTING.md). diff --git a/doc/Notes.md b/doc/Notes.md deleted file mode 100644 index 5aa66fb3..00000000 --- a/doc/Notes.md +++ /dev/null @@ -1,44 +0,0 @@ -## Mobile music apps - -* [Lemur](http://liine.net/en/products/lemur/) -* [Reactable Mobile](http://www.reactable.com/products/mobile/) - -## Memory allocation - -We're looking for a fast user-space storage allocator. - -Here are some options: - -Link License Notes ----- ------- ----- -[RTMalloc][] GPL/LGPL Two-level segregated fit, high performance -[tlsf][] PD Another TLSF implementation -[BGET][] Public Domain First fit or best fit, performance? -[dlmalloc][] PD Best fit, not threadsafe, realtime safe? -[nedmalloc][] Boost dlmalloc derivate, multithreaded, fast, realtime safe? -[ptmalloc3][] PD? dlmalloc derivate, realtime safe? -[tcmalloc][] ? Includes memory profiler, realtime safe? -[BSA++][] GPL Adaptive allocator - -[BGET]: http://www.fourmilab.ch/bget/ -[RTMalloc]: http://rtportal.upv.es/rtmalloc/ -[dlmalloc]: http://gee.cs.oswego.edu/dl/html/malloc.html -[nedmalloc]: http://www.nedprod.com/programs/portable/nedmalloc/index.html -[ptmalloc3]: http://www.malloc.de/ -[tcmalloc]: http://code.google.com/p/google-perftools/ -[tlsf]: http://tlsf.baisoku.org/ -[BSA++]: http://www.ercoppa.org/malloc/bsapp.htm - -### Literature - -See [IBM Inside memory management](http://www.ibm.com/developerworks/linux/library/l-memory/) for an overview and also the seminal paper [Dynamic Storage Allocation: A Survey and Critical Review][Wilson95] by Wilson et. al. - -[Wilson95]: ftp://osinside.net/pub/DynamicStorageAllocationSurvey-.pdf - -## Concurrency - -Here's an implementation of an [AtomicDouble](http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/extra/AtomicDouble.java?view=markup) in Java by Doug Lea, useful as a synchronization primitive for control buses, where a spinlock would be overkill. - -Here's an [interesting document](http://download.intel.com/technology/itj/2007/v11i4/5-foundations/5-Foundations_for_Scalable_Multi-core_Software.pdf) about Intel's [Threading Building Blocks](http://threadingbuildingblocks.org/) (including a scalable memory allocator). - -Some [notes](http://stackoverflow.com/questions/3640853/performance-test-sem-t-v-s-dispatch-semaphore-t-and-pthread-once-t-v-s-dispat) about `sem_post` and `dispatch_semaphore` on osx, and the manual page for [`dispatch_semaphore_create`](http://developer.apple.com/library/mac/#documentation/Darwin/Reference/Manpages/man3/dispatch_semaphore_create.3.html). diff --git a/doc/diagrams/inputs_outputs_buses.svg b/doc/diagrams/inputs_outputs_buses.svg deleted file mode 100644 index ff57c8a7..00000000 --- a/doc/diagrams/inputs_outputs_buses.svg +++ /dev/null @@ -1,369 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - Hardware inputs - Hardware outputs - - - - - - - ... - ... - - - ... - ... - - - - - - ... - Buses - Root node - - - - Subgroup - - - - - Buffer list - Buffer list - Buffer list - - diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 00000000..94aa5cc9 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,36 @@ +# Architecture + +## Audio routing + +```mermaid +graph TB + HW_IN["Hardware Inputs"] + HW_OUT["Hardware Outputs"] + + subgraph Engine + ExtIn["External AudioBuses"] + subgraph Root["Root Group"] + subgraph Grp["Group"] + S1["Synth"] + S2["Synth"] + end + end + Int["Internal AudioBuses"] + ExtOut["External AudioBuses"] + end + + HW_IN --> ExtIn + ExtIn -->|read| S1 + ExtIn -->|read| S2 + S1 -->|write| Int + Int -->|read| S2 + S1 -->|write| ExtOut + S2 -->|write| ExtOut + ExtOut --> HW_OUT +``` + +- **External AudioBuses** map to hardware I/O channels (input or output). +- **Internal AudioBuses** route audio between Synths within the Engine. +- The **Root Group** is the top of the node tree. Groups nest arbitrarily; Synths are leaves. +- Each Synth maps its audio inputs and outputs to buses via `/synth/map/input` and `/synth/map/output` (see [`osc-api.md`](osc-api.md)). +``` diff --git a/doc/OSC API.md b/docs/osc-api.md similarity index 100% rename from doc/OSC API.md rename to docs/osc-api.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..d0ed6808 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,13 @@ +# Examples + +## thADDeus + +A multitouch sine synthesizer. Demonstrates engine setup and real-time synth instantiation using the sine plugin. + +Source: [`thADDeus/src/`](thADDeus/src/) + +## sampler + +A multitouch sampler. Demonstrates file-based playback using the disksampler and sampler plugins. + +Source: [`sampler/src/`](sampler/src/) From b5f9e1d01612d66e877eb1c3a48575dae1751844 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:22:20 +0200 Subject: [PATCH 02/12] Remove unused VERSION file (version is in CMakeLists.txt) --- VERSION | 1 - 1 file changed, 1 deletion(-) delete mode 100644 VERSION diff --git a/VERSION b/VERSION deleted file mode 100644 index 0d91a54c..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.3.0 From 30b1c0a2021091f89360c0477579652816f6d8e6 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:27:31 +0200 Subject: [PATCH 03/12] Bump version to 0.4.0, add changelog in Keep a Changelog format --- CHANGELOG.md | 107 ++++++++++++++++++++++++++++++++++++++----------- CMakeLists.txt | 2 +- 2 files changed, 85 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46129f56..bfe3b514 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,84 @@ -### 0.3.0 - -* Add playback rate control to disksampler -* Add node placement options to node creation API commands. `Methcla::NodePlacement` can be used to control node placement in the C++ API. -* Remove `Methcla_Resource` from plugin API: Remove argument from `Methcla_SynthDef::construct` and rename `methcla_world_resource_retain`/`methcla_world_resource_release` to `methcla_world_synth_retain`/`methcla_world_synth_release` -* Implement various node placement options -* Allow plugins to notify the engine that they are done processing; add `methcla_world_synth_done` function to plugin API -* Fix bus zeroing logic (#102) -* Add operator bool to `Methcla::NodeId` -* Add `Methcla::Engine::nodeEndedHandler` API method returning `/node/ended` notification handler -* Add `ExponentialFade` plugin (`METHCLA_PLUGINS_EXPONENTIAL_FADE_URI`) to `methcla_plugins_node_control` library -* Fix bug in linked list implementation when adding a node before or after an existing node - -### 0.2.0 - -* Rename `Methcla::Engine::freeNode` to `Methcla::Engine::free` -* Use `Methcla::Engine::Bundle` instead of `Methcla::Engine::Request` -* Move plugin includes to `` -* **[Pro]** Include ExtAudioFile soundfile API -* Split synth creation into `/synth/new` (`Methcla::Engine::synth`) and `/synth/activate` (`Methcla::Engine::activate`) -* Refactor engine interface: Rename `Methcla::Engine::Request` to `Methcla::Request` and remove `Methcla::Engine::Bundle`. `Methcla::Request` now has `openBundle`/`closeBundle` methods for creating (nested) bundle structures. -* Add function for querying library version (`methcla_version`, `Methcla::version()`) -* Add function for changing debug logging behaviour (`methcla_engine_set_log_flags`, `Methcla::Engine::setLogFlags`) +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.4.0] + +### Added + +- Install and export CMake targets; `methcla::methcla` now available via `find_package` (#123) +- `METHCLA_BUILD_TESTS` CMake option, defaults to on when building as top-level project (#122) +- CMake presets (`debug`, `release`) replacing ad-hoc build configuration +- GitHub Actions CI replacing Travis CI +- `CONTRIBUTING.md` with build, test, and PR workflow instructions +- `docs/architecture.md` with Mermaid audio routing diagram +- `docs/osc-api.md` (moved from `doc/OSC API.md`) +- `examples/README.md` describing the thADDeus and sampler examples + +### Changed + +- Dependency management modernised: `FetchContent` for oscpp and googletest; Boost 1.91.0 vendored with `methcla_boost` namespace to avoid symbol collisions (#129) +- C++ standard raised to C++17 +- `Driver::Options` channel and buffer size fields changed from `int` to `size_t` (#80) +- Plugin libraries now build as CMake `MODULE` targets; soundfile API plugin selected per platform (#121) +- CMake target structure cleaned up: proper `PUBLIC`/`PRIVATE` dependencies, `methcla::methcla` alias target (#120) +- Warning flags scoped per-target via `methcla_target_warnings()` (#120) +- README rewritten: corrects platform scope (macOS and Linux desktop), updates build instructions, adds usage snippet + +### Fixed + +- Rounding, float precision, and type safety issues (#135) +- Implicit `NodeId` conversions broken by explicit constructor (#81) +- Missing `#include` directives causing build failures with GCC 13 and recent Clang +- GCC 13 false positive warnings from Boost lockfree and tinydir + +### Removed + +- Dead platform code and unused vendored libraries (Android, iOS, PNaCl, NaCl) (#128) +- `VERSION` file (version is solely in `CMakeLists.txt`) +- Stale `doc/Notes.md` and `doc/diagrams/` + +## [0.3.0] + +### Added + +- Playback rate control to disksampler +- Node placement options to node creation API commands (`Methcla::NodePlacement`) +- `methcla_world_synth_done` function to plugin API to notify the engine when processing is finished +- `operator bool` to `Methcla::NodeId` +- `Methcla::Engine::nodeEndedHandler` API method returning `/node/ended` notification handler +- `ExponentialFade` plugin (`METHCLA_PLUGINS_EXPONENTIAL_FADE_URI`) to `methcla_plugins_node_control` library + +### Fixed + +- Bus zeroing logic (#102) +- Bug in linked list implementation when adding a node before or after an existing node + +### Removed + +- `Methcla_Resource` from plugin API: removed argument from `Methcla_SynthDef::construct`; renamed `methcla_world_resource_retain`/`methcla_world_resource_release` to `methcla_world_synth_retain`/`methcla_world_synth_release` + +## [0.2.0] + +### Added + +- Function for querying library version (`methcla_version`, `Methcla::version()`) +- Function for changing debug logging behaviour (`methcla_engine_set_log_flags`, `Methcla::Engine::setLogFlags`) +- ExtAudioFile soundfile API (macOS) + +### Changed + +- Split synth creation into `/synth/new` (`Methcla::Engine::synth`) and `/synth/activate` (`Methcla::Engine::activate`) +- Refactored engine interface: renamed `Methcla::Engine::Request` to `Methcla::Request`, removed `Methcla::Engine::Bundle`; `Methcla::Request` now has `openBundle`/`closeBundle` for nested bundle structures +- Renamed `Methcla::Engine::freeNode` to `Methcla::Engine::free` +- Moved plugin includes to `` + +[unreleased]: https://github.com/samplecount/methcla/compare/v0.4.0...HEAD +[0.4.0]: https://github.com/samplecount/methcla/compare/v0.3.0...v0.4.0 +[0.3.0]: https://github.com/samplecount/methcla/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/samplecount/methcla/releases/tag/v0.2.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 58d11b5a..96aed9af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.24) -project(methcla VERSION 0.3.0) +project(methcla VERSION 0.4.0) set(METHCLA_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") From 89ee9e0399b533fbc8da44eac5a71d7b66171251 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:35:01 +0200 Subject: [PATCH 04/12] Add release script and changelog/release instructions --- CLAUDE.md | 2 ++ CONTRIBUTING.md | 15 ++++++++ tools/release.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100755 tools/release.py diff --git a/CLAUDE.md b/CLAUDE.md index 2779616b..5ce3ae24 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -85,6 +85,8 @@ Single-context layout: one `CONTEXT.md` + `docs/adr/` at the repo root. See `doc - When merging PRs via gh, update the working copy accordingly (pull and delete PR branch). - Keep PR branch name descriptive but short. No prefixes like `feat/`. Dashes: `my-awesome-new-feature`. - See `CONTRIBUTING.md` for the human-facing version of these workflows. +- When adding features or fixes, add an entry under `## [Unreleased]` in `CHANGELOG.md` using the standard sections: Added, Changed, Deprecated, Removed, Fixed, Security. +- To cut a release: run `tools/release.py ` then `git push && git push --tags`. ## Domain and API references diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 71097625..712a3b43 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,3 +20,18 @@ Always build and run tests before submitting changes. ## Code style Match the style of the surrounding code. C++17, C99. No new dependencies without discussion. + +## Changelog + +Add an entry under `## [Unreleased]` in `CHANGELOG.md` for every feature, fix, or notable change, +using the standard sections: Added, Changed, Deprecated, Removed, Fixed, Security. + +## Releasing + +1. Ensure `## [Unreleased]` in `CHANGELOG.md` is up to date. +2. Run `tools/release.py ` (e.g. `tools/release.py 1.2.3`). + - Bumps the version in `CMakeLists.txt` + - Moves `[Unreleased]` content to `[version]` in `CHANGELOG.md` + - Commits both files and creates an annotated git tag +3. Push: `git push && git push --tags` +4. Create a GitHub release from the tag, pasting the changelog entry as the description. diff --git a/tools/release.py b/tools/release.py new file mode 100755 index 00000000..b1e6993a --- /dev/null +++ b/tools/release.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Bump version, update changelog, commit, and tag a release.""" + +import re +import subprocess +import sys +from pathlib import Path + + +def run(cmd): + subprocess.run(cmd, check=True) + + +def get_output(cmd): + return subprocess.run(cmd, check=True, capture_output=True, text=True).stdout.strip() + + +def github_url(): + remote = get_output(["git", "remote", "get-url", "origin"]) + remote = re.sub(r"\.git$", "", remote) + remote = re.sub(r"^git@github\.com:", "https://github.com/", remote) + return remote + + +def bump_cmake(root: Path, version: str): + path = root / "CMakeLists.txt" + updated, n = re.subn( + r"(project\(methcla VERSION )\S+(\))", + rf"\g<1>{version}\2", + path.read_text(), + ) + if not n: + sys.exit("Error: could not find version in CMakeLists.txt") + path.write_text(updated) + + +def update_changelog(root: Path, version: str, repo_url: str) -> str: + path = root / "CHANGELOG.md" + text = path.read_text() + + prev = re.search(r"^## \[(\d+\.\d+\.\d+)\]", text, re.MULTILINE) + if not prev: + sys.exit("Error: could not find previous release version in CHANGELOG.md") + prev_version = prev.group(1) + + # Insert new version heading after [Unreleased] + text = text.replace( + "## [Unreleased]\n", + f"## [Unreleased]\n\n## [{version}]\n", + 1, + ) + + # Update [unreleased] comparison link + text = re.sub( + r"(?im)^\[unreleased\]: \S+", + f"[unreleased]: {repo_url}/compare/v{version}...HEAD", + text, + ) + + # Insert new version link above the previous version's link + text = text.replace( + f"[{prev_version}]:", + f"[{version}]: {repo_url}/compare/v{prev_version}...v{version}\n[{prev_version}]:", + 1, + ) + + path.write_text(text) + return prev_version + + +def main(): + if len(sys.argv) != 2 or not re.fullmatch(r"\d+\.\d+\.\d+", sys.argv[1]): + sys.exit("Usage: tools/release.py (e.g. 1.2.3)") + + version = sys.argv[1] + root = Path(get_output(["git", "rev-parse", "--show-toplevel"])) + repo_url = github_url() + + bump_cmake(root, version) + prev_version = update_changelog(root, version, repo_url) + + run(["git", "add", "CMakeLists.txt", "CHANGELOG.md"]) + run(["git", "commit", "-m", f"Release v{version}"]) + run(["git", "tag", "-a", f"v{version}", "-m", f"Release v{version}"]) + + print(f"\nReleased v{version} (previous: v{prev_version})") + print("Push with:\n git push && git push --tags") + + +if __name__ == "__main__": + main() From 71d586d499c5d488f63b0401f43964e64a611b75 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:40:16 +0200 Subject: [PATCH 05/12] Mention release preset in build instructions --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 712a3b43..ab75e9d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,8 +3,8 @@ ## Build and test ```sh -cmake --preset debug -cmake --build build/debug +cmake --preset debug # or: release +cmake --build build/debug # or: build/release ctest --test-dir build/debug --output-on-failure ``` From 5eda16d65cd545ac9147ed1329a5b6252cc7221c Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:43:29 +0200 Subject: [PATCH 06/12] Move all changes to [Unreleased]; revert version bump to 0.3.0 --- CHANGELOG.md | 35 ++++++++++------------------------- CMakeLists.txt | 2 +- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe3b514..b07a3e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.4.0] - ### Added +- Playback rate control to disksampler +- Node placement options to node creation API commands (`Methcla::NodePlacement`) +- `methcla_world_synth_done` function to plugin API to notify the engine when processing is finished +- `operator bool` to `Methcla::NodeId` +- `Methcla::Engine::nodeEndedHandler` API method returning `/node/ended` notification handler +- `ExponentialFade` plugin (`METHCLA_PLUGINS_EXPONENTIAL_FADE_URI`) to `methcla_plugins_node_control` library - Install and export CMake targets; `methcla::methcla` now available via `find_package` (#123) - `METHCLA_BUILD_TESTS` CMake option, defaults to on when building as top-level project (#122) - CMake presets (`debug`, `release`) replacing ad-hoc build configuration @@ -32,6 +36,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Bus zeroing logic (#102) +- Bug in linked list implementation when adding a node before or after an existing node - Rounding, float precision, and type safety issues (#135) - Implicit `NodeId` conversions broken by explicit constructor (#81) - Missing `#include` directives causing build failures with GCC 13 and recent Clang @@ -39,30 +45,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed +- `Methcla_Resource` from plugin API: removed argument from `Methcla_SynthDef::construct`; renamed `methcla_world_resource_retain`/`methcla_world_resource_release` to `methcla_world_synth_retain`/`methcla_world_synth_release` - Dead platform code and unused vendored libraries (Android, iOS, PNaCl, NaCl) (#128) - `VERSION` file (version is solely in `CMakeLists.txt`) - Stale `doc/Notes.md` and `doc/diagrams/` -## [0.3.0] - -### Added - -- Playback rate control to disksampler -- Node placement options to node creation API commands (`Methcla::NodePlacement`) -- `methcla_world_synth_done` function to plugin API to notify the engine when processing is finished -- `operator bool` to `Methcla::NodeId` -- `Methcla::Engine::nodeEndedHandler` API method returning `/node/ended` notification handler -- `ExponentialFade` plugin (`METHCLA_PLUGINS_EXPONENTIAL_FADE_URI`) to `methcla_plugins_node_control` library - -### Fixed - -- Bus zeroing logic (#102) -- Bug in linked list implementation when adding a node before or after an existing node - -### Removed - -- `Methcla_Resource` from plugin API: removed argument from `Methcla_SynthDef::construct`; renamed `methcla_world_resource_retain`/`methcla_world_resource_release` to `methcla_world_synth_retain`/`methcla_world_synth_release` - ## [0.2.0] ### Added @@ -78,7 +65,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Renamed `Methcla::Engine::freeNode` to `Methcla::Engine::free` - Moved plugin includes to `` -[unreleased]: https://github.com/samplecount/methcla/compare/v0.4.0...HEAD -[0.4.0]: https://github.com/samplecount/methcla/compare/v0.3.0...v0.4.0 -[0.3.0]: https://github.com/samplecount/methcla/compare/v0.2.0...v0.3.0 +[unreleased]: https://github.com/samplecount/methcla/compare/v0.2.0...HEAD [0.2.0]: https://github.com/samplecount/methcla/releases/tag/v0.2.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 96aed9af..58d11b5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.24) -project(methcla VERSION 0.4.0) +project(methcla VERSION 0.3.0) set(METHCLA_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") From 5340d04ded03b725af6c3e97ff6f93f57687ed21 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:46:12 +0200 Subject: [PATCH 07/12] Move release.py and copy-boost.sh to scripts/, remove clang-format --- {tools => scripts}/copy-boost.sh | 0 {tools => scripts}/release.py | 0 tools/clang-format | 23 ----------------------- 3 files changed, 23 deletions(-) rename {tools => scripts}/copy-boost.sh (100%) rename {tools => scripts}/release.py (100%) delete mode 100755 tools/clang-format diff --git a/tools/copy-boost.sh b/scripts/copy-boost.sh similarity index 100% rename from tools/copy-boost.sh rename to scripts/copy-boost.sh diff --git a/tools/release.py b/scripts/release.py similarity index 100% rename from tools/release.py rename to scripts/release.py diff --git a/tools/clang-format b/tools/clang-format deleted file mode 100755 index 4c7b6c1a..00000000 --- a/tools/clang-format +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# clang-format all C/C++/ObjC source files under source control - -function source_files() -{ - git ls-tree -r HEAD --name-only \ - | awk '!/^external_libraries/ && /\.(h|hpp|c|cpp|m|M)$/' -} - -function clang_format() -{ - xargs -n1 clang-format -style=file "$@" -} - -case "$1" in - check) - source_files | clang_format -output-replacements-xml - ;; - *) - source_files | clang_format -i - ;; -esac - From 866bcad07fd1d504d4a3477e63e969109a0f8a98 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:50:40 +0200 Subject: [PATCH 08/12] Move dumposcfile to tools/dumposcfile/, add CMake target, remove Makefile --- CMakeLists.txt | 3 +++ tools/CMakeLists.txt | 1 + tools/Makefile | 2 -- tools/dumposcfile/CMakeLists.txt | 3 +++ tools/{ => dumposcfile}/dumposcfile.cpp | 11 ++++------- 5 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 tools/CMakeLists.txt delete mode 100644 tools/Makefile create mode 100644 tools/dumposcfile/CMakeLists.txt rename tools/{ => dumposcfile}/dumposcfile.cpp (77%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58d11b5a..ed658cea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,9 @@ add_subdirectory(src) if(METHCLA_BUILD_TESTS) add_subdirectory(tests) endif() +if(PROJECT_IS_TOP_LEVEL) + add_subdirectory(tools) +endif() install(EXPORT methclaTargets NAMESPACE methcla:: diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000..a950150e --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(dumposcfile) diff --git a/tools/Makefile b/tools/Makefile deleted file mode 100644 index bed44b3a..00000000 --- a/tools/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -../build/dumposcfile: dumposcfile.cpp - c++ -std=c++11 -stdlib=libc++ -I../include -o $@ $? diff --git a/tools/dumposcfile/CMakeLists.txt b/tools/dumposcfile/CMakeLists.txt new file mode 100644 index 00000000..d81c17fb --- /dev/null +++ b/tools/dumposcfile/CMakeLists.txt @@ -0,0 +1,3 @@ +add_executable(dumposcfile dumposcfile.cpp) +target_link_libraries(dumposcfile PRIVATE oscpp::oscpp) +methcla_target_warnings(dumposcfile) diff --git a/tools/dumposcfile.cpp b/tools/dumposcfile/dumposcfile.cpp similarity index 77% rename from tools/dumposcfile.cpp rename to tools/dumposcfile/dumposcfile.cpp index 1b2de9a4..b770c1ca 100644 --- a/tools/dumposcfile.cpp +++ b/tools/dumposcfile/dumposcfile.cpp @@ -1,5 +1,3 @@ -// c++ -std=c++11 -stdlib=libc++ -I include -o build/dumposcfile -// tools/dumposcfile.cpp #include #include @@ -31,14 +29,13 @@ int main(int argc, const char* const* argv) while (true) { - int32_t size; - size_t n = fread(&size, sizeof(size), 1, file); - if (n != 1) + int32_t rawSize; + if (fread(&rawSize, sizeof(rawSize), 1, file) != 1) break; - size = OSCPP::convert32(size); + const size_t size = static_cast(OSCPP::convert32(rawSize)); if (buffer.size() < size) buffer.resize(size); - n = fread(buffer.data(), 1, size, file); + const size_t n = fread(buffer.data(), 1, size, file); if (n != size) throw std::runtime_error("Couldn't read packet"); OSCPP::Server::Packet packet(buffer.data(), size); From 9cc0bcddf1300f5c0f2f99cd270862218e035d15 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 00:51:55 +0200 Subject: [PATCH 09/12] Update release script path to scripts/release.py --- CLAUDE.md | 2 +- CONTRIBUTING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5ce3ae24..6f6fa15f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -86,7 +86,7 @@ Single-context layout: one `CONTEXT.md` + `docs/adr/` at the repo root. See `doc - Keep PR branch name descriptive but short. No prefixes like `feat/`. Dashes: `my-awesome-new-feature`. - See `CONTRIBUTING.md` for the human-facing version of these workflows. - When adding features or fixes, add an entry under `## [Unreleased]` in `CHANGELOG.md` using the standard sections: Added, Changed, Deprecated, Removed, Fixed, Security. -- To cut a release: run `tools/release.py ` then `git push && git push --tags`. +- To cut a release: run `scripts/release.py ` then `git push && git push --tags`. ## Domain and API references diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ab75e9d6..8f861a09 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,7 +29,7 @@ using the standard sections: Added, Changed, Deprecated, Removed, Fixed, Securit ## Releasing 1. Ensure `## [Unreleased]` in `CHANGELOG.md` is up to date. -2. Run `tools/release.py ` (e.g. `tools/release.py 1.2.3`). +2. Run `scripts/release.py ` (e.g. `scripts/release.py 1.2.3`). - Bumps the version in `CMakeLists.txt` - Moves `[Unreleased]` content to `[version]` in `CHANGELOG.md` - Commits both files and creates an annotated git tag From b794ec35d921d9f18cf9ddb50452b596f4f00312 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 01:00:29 +0200 Subject: [PATCH 10/12] Add docs/coding-style.md, link from CLAUDE.md and CONTRIBUTING.md --- CLAUDE.md | 1 + CONTRIBUTING.md | 2 +- docs/coding-style.md | 64 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 docs/coding-style.md diff --git a/CLAUDE.md b/CLAUDE.md index 6f6fa15f..d440ec3f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,3 +94,4 @@ Single-context layout: one `CONTEXT.md` + `docs/adr/` at the repo root. See `doc - Before changing build structure or dependencies, check `docs/adr/` for prior decisions. - When working with synth, node, or bus commands, consult `docs/osc-api.md`. - For audio routing and node tree structure, see `docs/architecture.md`. +- Follow `docs/coding-style.md` for all code changes. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8f861a09..63d87cd7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ Always build and run tests before submitting changes. ## Code style -Match the style of the surrounding code. C++17, C99. No new dependencies without discussion. +See [`docs/coding-style.md`](docs/coding-style.md). C++17, C99. No new dependencies without discussion. ## Changelog diff --git a/docs/coding-style.md b/docs/coding-style.md new file mode 100644 index 00000000..459c0ab4 --- /dev/null +++ b/docs/coding-style.md @@ -0,0 +1,64 @@ +# Coding style + +## General + +- Avoid global state. Prefer passing dependencies explicitly. +- No magic numbers — use named constants or enums. +- Match the style of the surrounding code when in doubt. + +## C++ + +### Naming + +| Kind | Convention | Example | +|---|---|---| +| Types, classes | `PascalCase` | `NodeId`, `AudioBus` | +| Functions, methods | `camelCase` | `addToHead`, `isGroup` | +| Member variables | `m_` prefix | `m_first`, `m_engine` | +| Enum constants | `kMethcla_` prefix | `kMethcla_NodePlacementHeadOfGroup` | +| Macros | `METHCLA_` prefix | `METHCLA_EXPORT` | + +### Types and variables + +- Use `const` on variables, parameters, and member functions wherever possible. +- Use `constexpr` instead of `const` for compile-time constants. +- Use `nullptr`, not `NULL` or `0` for pointers. +- Use `size_t` for sizes and counts. Cast once at the boundary; don't scatter casts. +- Use `static_cast` for explicit conversions. Avoid C-style casts. + +### Classes + +- Mark overriding methods `override`. Do not also mark them `virtual`. + +### Headers + +- Header guards: `#ifndef METHCLA__HPP_INCLUDED` / `#define` / `#endif`. +- Include order: own header first, then internal headers, then third-party, then standard library. Each group separated by a blank line. + +### Namespaces + +- `Methcla::` for C++ API. +- Anonymous namespace for translation-unit-local helpers instead of `static`. + +### Formatting + +- 4-space indentation, no tabs. +- Allman brace style for functions and control flow (opening brace on its own line). +- Braces on the same line for namespace and class bodies. + +## C API + +- All public symbols prefixed `methcla_` (functions) or `Methcla_` (types). +- Enum constants prefixed `kMethcla_`. +- `typedef struct` and `typedef enum` — no anonymous structs. +- `extern "C"` guards in every public header. +- `METHCLA_EXPORT` on all exported symbols. +- `snake_case` for all identifiers. +- Header guards: `#ifndef METHCLA__H_INCLUDED`. + +## Python + +- Type annotations on all function signatures. +- `pathlib.Path` for file paths, not string concatenation. +- `subprocess.run(..., check=True)` — don't silently swallow non-zero exit codes. +- Keep scripts short and single-purpose. Extract helpers only when reused. From 2a4d53ba7da13edecdc7c93c561c0dbc485fa840 Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 01:05:26 +0200 Subject: [PATCH 11/12] Add pre-commit config for clang-format and black --- .pre-commit-config.yaml | 10 ++++++++++ CLAUDE.md | 2 +- CONTRIBUTING.md | 15 +++++++++++++++ docs/coding-style.md | 8 +++++--- 4 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..718b19b0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v19.1.7 + hooks: + - id: clang-format + + - repo: https://github.com/psf/black + rev: 25.1.0 + hooks: + - id: black diff --git a/CLAUDE.md b/CLAUDE.md index d440ec3f..f297ae0b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,7 +81,7 @@ Single-context layout: one `CONTEXT.md` + `docs/adr/` at the repo root. See `doc - Create PRs for non-trivial changes. This allows reviewing code and running actions before merging. - Create PR branches before submitting a PR or when prompted to submit changes to a PR branch. - For small, low-risk changes, or when prompted to do so, commit directly to develop. -- Before committing changes or submitting PRs, build locally and run tests. +- Before committing changes or submitting PRs, run `pre-commit run --all-files` to format code, then build locally and run tests. - When merging PRs via gh, update the working copy accordingly (pull and delete PR branch). - Keep PR branch name descriptive but short. No prefixes like `feat/`. Dashes: `my-awesome-new-feature`. - See `CONTRIBUTING.md` for the human-facing version of these workflows. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 63d87cd7..847ae705 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,20 @@ # Contributing +## Setup + +Install the pre-commit hooks after cloning: + +```sh +pip install pre-commit # or: brew install pre-commit +pre-commit install +``` + +Hooks run automatically on `git commit`. To format all files manually: + +```sh +pre-commit run --all-files +``` + ## Build and test ```sh diff --git a/docs/coding-style.md b/docs/coding-style.md index 459c0ab4..bd644ac9 100644 --- a/docs/coding-style.md +++ b/docs/coding-style.md @@ -42,9 +42,7 @@ ### Formatting -- 4-space indentation, no tabs. -- Allman brace style for functions and control flow (opening brace on its own line). -- Braces on the same line for namespace and class bodies. +Enforced by clang-format. Configuration is in `.clang-format`. Run `pre-commit run --all-files` rather than formatting manually. ## C API @@ -62,3 +60,7 @@ - `pathlib.Path` for file paths, not string concatenation. - `subprocess.run(..., check=True)` — don't silently swallow non-zero exit codes. - Keep scripts short and single-purpose. Extract helpers only when reused. + +### Formatting + +Enforced by black. Run `pre-commit run --all-files` rather than formatting manually. From 34cbfe32ab2516f5234890257c6282177bdafe0d Mon Sep 17 00:00:00 2001 From: Stefan Kersten Date: Sun, 24 May 2026 01:07:15 +0200 Subject: [PATCH 12/12] Add missing #include to dumposcfile.cpp --- tools/dumposcfile/dumposcfile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/dumposcfile/dumposcfile.cpp b/tools/dumposcfile/dumposcfile.cpp index b770c1ca..07f3d04d 100644 --- a/tools/dumposcfile/dumposcfile.cpp +++ b/tools/dumposcfile/dumposcfile.cpp @@ -1,5 +1,6 @@ #include +#include #include #include