Skip to content

0.3.0

Choose a tag to compare

@github-actions github-actions released this 20 Sep 19:43
· 16 commits to main since this release

Changes since the last Release

Fixed

  • MHS REPL Echoed Every Line Twice And Printed Results Late: psnd's editor draws the line, then writes it into the PTY, where the terminal driver echoes it back and psnd printed that too. Output was also collected after a fixed 100ms sleep rather than read until MicroHs prompted again, so a result slower than that -- midiInit opening CoreMIDI, for one -- appeared under the next prompt, and the echo split across reads showed up as lines missing their first character. psnd now matches the echo against what it just sent and drops it, and drains the PTY until the prompt returns or 5s pass, so a result lands under the line that produced it (repl.c)

  • Schedule Growth Ignored realloc Failure: schedule_grow() assigned the result of realloc() straight to sched->events and raised capacity whether or not the allocation succeeded, so a failed grow lost the existing buffer and the caller then wrote an event through a null pointer. It now grows through a temporary, rejects a capacity whose byte size would overflow size_t, and reports failure; all thirteen scheduling entry points drop the event instead of writing past the buffer (shared_async.c)

  • Tracker Gate Wrapped Instead Of Clamping: tracker_notes_parse_gate() cast an unbounded int to its int16_t output, so ~65536 became a gate of 0 and other values became negative durations. Gates now clamp to INT16_MAX, matching the clamp velocity already applies at 127. parse_int() saturates as well, having been undefined behaviour on any expression carrying more digits than an int holds (tracker_plugin_notes.c)

  • MHS Package Preload Grew .mhscache Without Bound: psnd passed -pbase -pmusic on every mhs invocation. MicroHs appends preloaded packages to the cache it just read without checking whether they are already there, so each run added ~2.3MB and about a second of load time: 3s at run 1, 25s by run 12, and eventually a mhs_smoke_tests timeout whose kill left a zero-length cache that every later run aborted on. The packages survive in the cache, so psnd now preloads only when the cache is missing, empty, or older than the binary, and deletes the cache in the latter two cases rather than mixing package copies. mhs_cache_needs_preload() sits in vfs.c so the standalone mhs-midi variants get the same guard. Upstream MicroHs has the same unconditional addPackage on master, so the version bump does not fix it (vfs.c, repl.c, mhs_midi_standalone_main.c)

  • Edited MHS Files Kept Running Their Old Code: psnd mhs -r file.hs and psnd play file.hs served whatever was compiled the first time, until .mhscache was deleted by hand. MicroHs keys a cached module by the identifier the command line gave it, so a file argument is stored under file.hs while validateCache looks the entry up under the module's declared name, finds nothing, and checksums nothing; passing the same module by name validates correctly. psnd now reads the declared module name out of the file and passes it with -i for the directory the name implies, falling back to the path when the file does not sit where its module name says it should, which is the one case that stays stale. mhs_reload_tests covers it: run a module, rewrite it, run again, assert the new output. Present since MicroHs 0.15.0.0, reported upstream with a patch in patch-mhs-validate-file-arg.diff (repl.c)

  • Every MHS Run Rewrote The Whole Compile Cache: psnd passed -C, so each run re-serialized .mhscache even when nothing in it changed. MicroHs 0.16 compresses that file with LZMA instead of LZ77, which put ~60% of a warm run inside the LZMA encoder: 1.85s of the 2.15s upstream mhs spends on one warm file, against 0.30s with -CR. psnd now passes -CR on a warm cache and -C only when cold, keyed off the same test that gates the preload flags. A warm psnd mhs -r Smoke.hs is 0.41s, from 4.25s; mhs_smoke_tests is 0.49s, from 4.37s. A warm run no longer persists a module it just compiled, which costs 0.3s on an unchanged small file and nothing on a changed one (vfs.c, vfs.h, repl.c, mhs_midi_standalone_main.c)

  • macOS Link Flags Overflowed The MHS argv Array: LINK_EXTRA_ARGS was 14 in both branches of its #ifdef __APPLE__, but the macOS branch pushes 20 entries -- three static libraries, three frameworks, and -lc++. Every psnd mhs -o exe file.hs wrote four pointers past the allocated argv and mhs then read heap garbage as a module name, failing with File not found: "(". The macOS count is now 20 (repl.c)

  • make test Had No Per-Test Timeout: a test blocking on stdin ran until the caller's own timeout with no indication of which test hung. The ten ctest targets now share one PSND_CTEST variable carrying --timeout 120, the ceiling scripts/build_release.py already used (Makefile)

Changed

  • Vendored MicroHs 0.15.0.0 -> 0.16.5.0: 553 upstream commits. The predicted blocker, 0.16's getPaths returning no package directory when MHSDIR is set, does not apply: -a fills pkgPaths independently and psnd already passes it. What the bump did need was mhs.conf, renamed from targets.conf upstream and now fatal rather than advisory on the compile-to-executable path, so mhs-embed gained --conf and serves it from the virtual root. Compilation itself is unchanged: the same six modules compile to C in 1.44s under 0.15.0.0 and 1.42s under 0.16.5.0, a three-line module in 0.50s under both, and a warm run that only reads the cache is 0.28s against 0.30s. What did change is the cache write, 0.71s against 1.88s, because 0.16 compresses .mhscache with LZMA rather than LZ77; the file is smaller for it, 730,587 bytes against 1,324,707. A cold psnd mhs -r pays that write once and went 2.77s -> 5.62s, while the warm path is faster than either version was, for the reasons in the two entries above. docs/dev/updating-microhs.md records the measurements and why Path A was taken over adopting upstream --embed-packages (source/thirdparty/MicroHs, mhs-embed.c, langs/mhs/CMakeLists.txt)

Added

  • Local Patches To Vendored MicroHs, Applied At Build Time: source/langs/mhs/patches/ holds fixes psnd needs before upstream takes them, with a README saying what each one is for. The build copies the vendored tree, patches the copy, and rebuilds the compiler from it with bin/mhs, so source/thirdparty/MicroHs is never modified. The first patch makes the REPL run an IO action that returns a value instead of refusing it: midiInit at the prompt reported Cannot satisfy constraint: Show (IO Bool), as did midiListPorts, midiIsOpen, midiRandom and the three record queries. Cost is one 39.7s self-compile, cached until a patch or bin/mhs changes; -DMHS_PATCH_MHS=OFF builds against upstream's committed generated/mhs.c (langs/mhs/CMakeLists.txt, patches/)

  • Third-Party License Texts In Release Archives: docs/licenses/ holds the license text of each vendored dependency, and build_release.py copies it into every archive, failing if the directory is absent. Archives carried only psnd's own LICENSE while the binaries link LGPL (Csound, FluidSynth, libsndfile, liblo) and GPL-2.0 (Ableton Link) code. zstd and JUCE ship no text in-tree and the mongoose GPL-2.0-only conflict still blocks the web variants; docs/licenses/README.md records both gaps (build_release.py)