diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 09f6598..f0e52c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,7 +2,7 @@ name: Tests # Documentation-only changes can't change these results, so they don't run them. # Past that, the scope job reads the diff and each job runs only for the paths it -# checks: ostinato for apps/ostinato and lib/ (its page loads studio's lib/), and +# checks: ostinato for programs/ostinato and lib/ (its page loads studio's lib/), and # engines for lib/, tests/ and this workflow (which holds its pins). A push to # main is scoped to what that push changed. A manual run, or a push with no # earlier commit to compare with, runs both. @@ -78,7 +78,7 @@ jobs: files=$(git diff --name-only "$BASE...$GITHUB_SHA") echo "$files" has() { grep -qE "$1" <<< "$files" && echo true || echo false; } - echo "ostinato=$(has '^(apps/ostinato/|lib/|\.github/workflows/tests\.yml$)')" >> "$GITHUB_OUTPUT" + echo "ostinato=$(has '^(programs/ostinato/|lib/|\.github/workflows/tests\.yml$)')" >> "$GITHUB_OUTPUT" echo "engines=$(has '^(lib/|tests/|\.github/workflows/tests\.yml$)')" >> "$GITHUB_OUTPUT" echo "synthstation=$(has '^(apps/synthstation/|lib/|\.github/workflows/tests\.yml$)')" >> "$GITHUB_OUTPUT" echo "appkit=$(has '^(lib/appkit/|tests/appkit/|\.github/workflows/tests\.yml$)')" >> "$GITHUB_OUTPUT" @@ -99,11 +99,11 @@ jobs: python-version: "3.x" - name: Vendor PyScript - working-directory: apps/ostinato + working-directory: programs/ostinato run: ./scripts/vendor_pyscript.sh - name: Ostinato's tests - working-directory: apps/ostinato + working-directory: programs/ostinato run: python -m unittest discover -s tests -v # Synth Station's tests on CPython, against controlkit from lib/, @@ -240,7 +240,7 @@ jobs: with: python-version: "3.13" - # Studio's controlkit tests import lvgl on CPython. + # The controlkit tests import lvgl on CPython. - name: Install LVGL for CPython run: | sudo apt-get update && sudo apt-get install -y libsdl2-2.0-0 @@ -287,7 +287,7 @@ jobs: make -C micropython/ports/unix submodules make -C micropython/ports/unix -j4 USER_C_MODULES=$GITHUB_WORKSPACE/modules - - name: Studio's tests + - name: The apps repo's tests working-directory: studio env: PYDEVICES_MICROPYTHON: ${{ github.workspace }}/micropython/ports/unix/build-standard/micropython diff --git a/.style-exclude b/.style-exclude index cd28c64..fc6f371 100644 --- a/.style-exclude +++ b/.style-exclude @@ -1,5 +1,5 @@ # Paths the style check skips: code that isn't ours, and generated files. # One glob per line. -# Written by apps/ostinato/scripts/make_icon_font.py. -apps/ostinato/app/ui/icons.py +# Written by programs/ostinato/scripts/make_icon_font.py. +programs/ostinato/app/ui/icons.py diff --git a/AGENTS.md b/AGENTS.md index 0657c70..b38da5f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,7 +5,7 @@ - The contract runs on MicroPython and CircuitPython too: no `os.path`, no f-strings with `=`, and `memoryview.cast` isn't there (use `array.array("h", bytes(buf))`). CircuitPython's audio buffers count samples, not bytes. - `tools/gate.py` drives Windows from WSL: REAPER, a loopback MIDI port and a CPython venv on Windows. A Windows process outlives a timeout on the WSL side; the gate ends them by command line. - REAPER sends CC0 = 0 to every part when it stops. What a part played is in `Part.played`, not in its bank at the end. -- Applications live in `apps/` (Ostinato, Vamp). Libraries live in `lib/` (soundmodule, songmodel). Code two of them share goes in `lib/` (`songmodel`); Ostinato's `tools/gen_boot.py` lists what its page loads from there, so run it after adding a shared file. +- Home apps (an `app.json`, run by appkit) live in `apps/`; things that run on their own (Ostinato, Vamp) live in `programs/`. Libraries live in `lib/` (soundmodule, songmodel). Code two of them share goes in `lib/` (`songmodel`); Ostinato's `tools/gen_boot.py` lists what its page loads from there, so run it after adding a shared file. - Vamp's tests need hmilink: `$HMILINK_PATH`, or a `pydevices` checkout beside this one. CI sets `VAMP_REQUIRE=1` so a missing piece fails instead of skipping. - sayso's tests: `PYTHONPATH=lib python -m unittest discover -s tests/sayso` from the root. The offline tests need no credentials; `test_live.py` skips without a `sayso.local.json`. Never commit `sayso.local.json`, `tokens.json` or `lib/sayso/examples/write_examples.json`. - To test sayso without a network, replace `sayso.transport.requests` with a mock that has `get`/`post` (and `put`/`delete` for writes) before you build `SpotifyClient`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7105255..e66e188 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ cloned beside this repository (so the real-instrument checks run): ```bash python -m unittest discover -s tests -cd apps/ostinato && python -m unittest discover -s tests +cd programs/ostinato && python -m unittest discover -s tests ``` MicroPython and CircuitPython runs get `MICROPYPATH` from the test wrapper @@ -45,7 +45,7 @@ bank at the end. The gate's docstring lists what it needs on Windows. ## Ostinato's checks -From `apps/ostinato`, run them all in one app session, about four minutes, against a real Chrome: +From `programs/ostinato`, run them all in one app session, about four minutes, against a real Chrome: ```bash .venv/bin/python tools/check_all.py --cdp http://127.0.0.1:9222 diff --git a/README.md b/README.md index f50cfc1..d405faa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Apps -The apps for PyDevices: things you run on a board or a desktop. +The apps for PyDevices: things you run on a board or a desktop. A Home app lives in `apps/`; a program, which runs on its own, lives in [`programs/`](programs/README.md). ## Install diff --git a/ROADMAP.md b/ROADMAP.md index 6d45647..80e2b09 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -27,7 +27,7 @@ keyboard sends it over MIDI, the way a hardware tone module does. - A published copy of the app on the web, so it installs without running a local server. -- The rest is in [Ostinato's roadmap](apps/ostinato/ROADMAP.md). +- The rest is in [Ostinato's roadmap](programs/ostinato/ROADMAP.md). ## Synth Station diff --git a/apps/midiengine/__init__.py b/apps/midiengine/__init__.py index d9e9e8e..4fea1cc 100644 --- a/apps/midiengine/__init__.py +++ b/apps/midiengine/__init__.py @@ -44,7 +44,7 @@ starts; a channel's instrument is made the first time it is played or given a program, and its samples are read on its notes, a bounded number of them kept. A tap on a piece only starts the player's clock: its first events go on the app's timer. -The MIDI it answers is the sound module's (studio's ``soundmodule.engine``). +The MIDI it answers is the sound module's (the apps repo's ``soundmodule.engine``). """ import lvgl as lv diff --git a/docs/get-started/first-program.md b/docs/get-started/first-program.md index 16c4677..2d104a0 100644 --- a/docs/get-started/first-program.md +++ b/docs/get-started/first-program.md @@ -3,9 +3,9 @@ [`tools/render_mid.py`](../../tools/render_mid.py) plays a Standard MIDI File through the sound module and writes the result to a WAV. It needs no MIDI hardware and no sound card, and it is the same engine that plays live MIDI. -You need [studio installed](install.md) and a SoundFont. +You need the [apps repo installed](install.md) and a SoundFont. -From the studio folder: +From the apps folder: ```bash python tools/render_mid.py tests/midi/musicbox1_spooky_waltz.mid take.wav --bank GeneralUser-GS.sf2 --path lib diff --git a/docs/get-started/install.md b/docs/get-started/install.md index e29602f..2f1d04d 100644 --- a/docs/get-started/install.md +++ b/docs/get-started/install.md @@ -1,11 +1,11 @@ # Install -studio is used from a clone, beside the libraries it builds on. The sound +The apps repo is used from a clone, beside the libraries it builds on. The sound module and Vamp need audiodsp, which provides the audio nodes, and audioinstruments and audioeffects, which provide the instruments and effects: ```bash -git clone https://github.com/PyDevices/studio +git clone https://github.com/PyDevices/apps git clone https://github.com/PyDevices/audioinstruments git clone https://github.com/PyDevices/audioeffects git clone https://github.com/PyDevices/audiodsp @@ -15,7 +15,7 @@ pip install --no-deps ./audioinstruments ./audioeffects/lib/audioeffects ## A SoundFont -The sound module plays General MIDI from a SoundFont, which studio does not +The sound module plays General MIDI from a SoundFont, which the apps repo does not ship. Download a General MIDI `.sf2`, such as [GeneralUser GS](https://schristiancollins.com/generaluser.php), and either pass it with `--bank`, set the `SOUNDFONT` environment variable, or put it in diff --git a/docs/guides/play-the-sound-module.md b/docs/guides/play-the-sound-module.md index dab63be..bf37314 100644 --- a/docs/guides/play-the-sound-module.md +++ b/docs/guides/play-the-sound-module.md @@ -5,7 +5,7 @@ module does. You set nothing up on it: each part's Bank Select and Program Change pick the sound, so a DAW's own instrument-selection UI is enough. The [reference](../reference/sound-module-midi.md) lists the banks. -You need [studio installed](../get-started/install.md), a SoundFont, and a +You need the [apps repo installed](../get-started/install.md), a SoundFont, and a virtual MIDI port your DAW can send to, such as a loopback port on Windows. ## Run it on a port diff --git a/docs/guides/put-vamp-on-a-board.md b/docs/guides/put-vamp-on-a-board.md index 83558c2..87b4121 100644 --- a/docs/guides/put-vamp-on-a-board.md +++ b/docs/guides/put-vamp-on-a-board.md @@ -6,9 +6,9 @@ Ostinato's window and a browser page edit the song it plays, live, over [hmilink](https://github.com/PyDevices/pydevices/tree/main/hmilink). A MIDI keyboard on the board plays the selected track. -You need [studio installed](../get-started/install.md), a General MIDI +You need the [apps repo installed](../get-started/install.md), a General MIDI SoundFont, [mpftp](https://github.com/PyDevices/mpftp) installed, and a clone -of [pydevices](https://github.com/PyDevices/pydevices) beside studio, where +of [pydevices](https://github.com/PyDevices/pydevices) beside the apps repo, where `tools/vamp_files.py` finds hmilink (`--hmilink DIR` names it otherwise). ## On a board @@ -29,17 +29,17 @@ import vamp; vamp.run() ## On a desktop MicroPython -With studio's `lib/`, hmilink, audioinstruments' and audioeffects' `lib/` and pydevices' `lib/` +With the apps repo's `programs/` and `lib/`, hmilink, audioinstruments' and audioeffects' `lib/` and pydevices' `lib/` on `MICROPYPATH`: ```bash -micropython -X heapsize=96M -m vamp.main --bank GeneralUser-GS.sf2 --song apps/ostinato/songs/sketches/soundfont_check.json +micropython -X heapsize=96M -m vamp.main --bank GeneralUser-GS.sf2 --song programs/ostinato/songs/sketches/soundfont_check.json ``` ## Open a panel Browse to `http://HOST:9100/` for the browser panel, or join with Ostinato's -window, from `apps/ostinato`: +window, from `programs/ostinato`: ```bash python -m ostinato --connect HOST:9100 @@ -47,7 +47,7 @@ python -m ostinato --connect HOST:9100 Every panel sees the same song, and an edit on one shows on the others while the box keeps playing. The options (`--port`, `--midi`, `--lookahead-ms`, -`--seconds`) are listed at the top of [`apps/vamp/main.py`](../../apps/vamp/main.py). +`--seconds`) are listed at the top of [`programs/vamp/main.py`](../../programs/vamp/main.py). ## Check that a box is behaving diff --git a/docs/guides/run-ostinato.md b/docs/guides/run-ostinato.md index c46d79e..f7691d7 100644 --- a/docs/guides/run-ostinato.md +++ b/docs/guides/run-ostinato.md @@ -2,9 +2,9 @@ Ostinato is a pattern sequencer and song editor. It runs in a browser, where you can install it from the address bar as an app that works offline, and as -a native window on the desktop. It lives in [`apps/ostinato`](../../apps/ostinato). +a native window on the desktop. It lives in [`programs/ostinato`](../../programs/ostinato). -The steps are in its [README](../../apps/ostinato/README.md), and every +The steps are in its [README](../../programs/ostinato/README.md), and every command there runs from that folder: - **In a browser.** Fetch the PyDevices wheels it runs on, vendor PyScript @@ -15,7 +15,7 @@ command there runs from that folder: Both read the song model in [`lib/songmodel`](../../lib/songmodel), which Vamp uses too. `tools/serve.py` serves that folder from this repository, so -run it from a studio checkout. +run it from an apps checkout. ## Use a SoundFont in Ostinato @@ -23,7 +23,7 @@ The two SoundFont instruments, **SOUNDFONT** and **SOUNDFONT DRUMS**, play whatever `.sf2` they find: the `SOUNDFONT` environment variable, else the first one in `%USERPROFILE%\soundfonts` (`~/soundfonts` elsewhere). Without one the track stays silent and the status line says so. -[`apps/ostinato/songs/sketches/soundfont_check.json`](../../apps/ostinato/songs/sketches/soundfont_check.json) +[`programs/ostinato/songs/sketches/soundfont_check.json`](../../programs/ostinato/songs/sketches/soundfont_check.json) plays all four tracks on them. ## Give it a second panel diff --git a/docs/how-it-works/how-its-checked.md b/docs/how-it-works/how-its-checked.md index 34741d8..96f9d60 100644 --- a/docs/how-it-works/how-its-checked.md +++ b/docs/how-it-works/how-its-checked.md @@ -1,6 +1,6 @@ # How it is checked -Every check in studio has been shown to fail on a fault planted on purpose. +Every check in the apps repo has been shown to fail on a fault planted on purpose. `tests/engine_contract.py` keeps a `PLANTS` table, one broken-on-purpose variant per check, and the suite runs the contract once per plant and expects it to fail. A check that has only ever passed proves nothing, so a new check @@ -46,6 +46,6 @@ turn. They need a Windows machine as well as WSL. Ostinato's timing is measured in Chrome, because headless Chromium's audio clock is not reliable under load. Its checks, which cover MIDI-to-sound latency, the editing workflows, file export and offline start, are listed in -the [Ostinato README](../../apps/ostinato/README.md#checks). +the [Ostinato README](../../programs/ostinato/README.md#checks). To run them yourself, see [CONTRIBUTING.md](../../CONTRIBUTING.md). diff --git a/docs/how-it-works/overview.md b/docs/how-it-works/overview.md index 4141db5..f1a1efa 100644 --- a/docs/how-it-works/overview.md +++ b/docs/how-it-works/overview.md @@ -1,14 +1,14 @@ -# What is in studio +# What is in the apps repo -studio holds three applications that share a song model and an audio stack. +The apps repo holds two programs and a sound module that share a song model and an audio stack. - **soundmodule** ([`lib/soundmodule`](../../lib/soundmodule)) is a MIDI tone module. Its engine has no face of its own: feed it MIDI bytes and pull its `output`, from a headless loop (`soundmodule.run`), a UI or a test. -- **Ostinato** ([`apps/ostinato`](../../apps/ostinato)) is the sequencer and +- **Ostinato** ([`programs/ostinato`](../../programs/ostinato)) is the sequencer and song editor. LVGL draws its interface in a browser worker or a desktop window, and audioinstruments makes the sound. -- **Vamp** ([`apps/vamp`](../../apps/vamp)) plays Ostinato's songs on a +- **Vamp** ([`programs/vamp`](../../programs/vamp)) plays Ostinato's songs on a MicroPython with no screen of its own. Its panels are the Ostinato window and a browser page, over the network. diff --git a/docs/index.md b/docs/index.md index 4cc2208..cee6c7b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -12,7 +12,7 @@ through the sound module. **Reference.** [The apps, and what each does](reference/apps.md). [What an appkit app must provide](reference/app-requirements.md). [The sound module's MIDI](reference/sound-module-midi.md): banks, controllers, System Exclusive, and the `soundmodule.run` options. Vamp's options are listed at the top of -[`apps/vamp/main.py`](../apps/vamp/main.py). +[`programs/vamp/main.py`](../programs/vamp/main.py). **How it works.** [How the apps are built](how-it-works/apps.md), [what is in the apps](how-it-works/overview.md) and [how it is checked](how-it-works/how-its-checked.md). diff --git a/docs/reference/apps.md b/docs/reference/apps.md index 5e279fd..48d2219 100644 --- a/docs/reference/apps.md +++ b/docs/reference/apps.md @@ -1,6 +1,6 @@ # Apps -What each app here does. Apps that run on a board are also listed in the +What each app in `apps/` does. These are the apps Home lists and appkit runs; things that run on their own, like Ostinato and Vamp, are in [programs](../../programs/README.md). Apps that run on a board are also listed in the [gallery](https://gallery.omnipython.org). | App | What it does | @@ -17,4 +17,3 @@ What each app here does. Apps that run on a board are also listed in the | [Sound card](../../apps/card) | The board is a USB speaker for a computer, with a spectrum of what plays. | | [System](../../apps/system) | Whether the board is well, at a glance: a turning arc and frame rate, a touch test, memory, boot, network, audio and what the board is; `stats()` gives the same from the REPL. | | [Wake word](../../apps/wake) | Listens on a USB microphone for "Hey Jarvis" and flashes on each detection. | -| [Ostinato](../../apps/ostinato) | A pattern sequencer and song editor in a browser or on the desktop. | diff --git a/lib/songmodel/__init__.py b/lib/songmodel/__init__.py index 75a409b..3b53179 100644 --- a/lib/songmodel/__init__.py +++ b/lib/songmodel/__init__.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MIT -"""The song both of studio's groovebox applications edit, and what carries it +"""The song both of this repo's groovebox applications edit, and what carries it to a front panel. - `songmodel.model`: the project, plain JSON data changed only by ops that @@ -12,7 +12,7 @@ be a front panel (needs ``hmilink``, from pydevices). - ``page.html``: the browser front panel a device serves on its hmilink port. -Ostinato (``apps/ostinato``) and Vamp (``apps/vamp``) both use it. +Ostinato (``programs/ostinato``) and Vamp (``programs/vamp``) both use it. """ diff --git a/programs/README.md b/programs/README.md new file mode 100644 index 0000000..d7a2c80 --- /dev/null +++ b/programs/README.md @@ -0,0 +1,14 @@ +# Programs + +A program runs on its own: you start it, and it doesn't need Home. A Home app +is different. It lives in [`apps/`](../apps), has an `app.json`, and appkit +runs it from the home screen. + +| Program | What it does | +|---|---| +| [Ostinato](ostinato) | A pattern sequencer and song editor, in a browser or a desktop window. | +| [Vamp](vamp) | Plays Ostinato's songs on one MicroPython, with the screens elsewhere. | + +Every program folder has a `README.md` that opens with what the program does, +then a `Runs on:` line naming the boards or computers it works on, then how to +put it there. diff --git a/apps/ostinato/.gitignore b/programs/ostinato/.gitignore similarity index 100% rename from apps/ostinato/.gitignore rename to programs/ostinato/.gitignore diff --git a/apps/ostinato/README.md b/programs/ostinato/README.md similarity index 94% rename from apps/ostinato/README.md rename to programs/ostinato/README.md index ef83849..e122c57 100644 --- a/apps/ostinato/README.md +++ b/programs/ostinato/README.md @@ -5,13 +5,15 @@ synth from a MIDI keyboard, put drums on a step grid, record loops and fix them in a piano roll, chain patterns into a song, put effects on each track, and export a WAV or a MIDI file. It installs as an app and works offline. +Runs on: a desktop browser (Chrome or Edge) or a Windows desktop with Python 3.14 or later. Put it there with the steps under [Run it](#run-it) or [Run it on the desktop](#run-it-on-the-desktop). + It's built from PyDevices: LVGL draws the interface, audioinstruments' 33 instruments and 44 effects make the sound, and `midiports` and `adafruit_midi` read the keyboard, all running in Pyodide in Web Workers. ## Run it -Every command here runs from this folder, `apps/ostinato` in studio. +Every command here runs from this folder, `programs/ostinato` in the apps repo. ```bash .venv/bin/python scripts/fetch_wheels.py # the PyDevices wheels it runs on @@ -65,7 +67,7 @@ venv above unless `PY315` names another interpreter. track's mute and volume, and a step grid. An edit on either shows on the other. `--device` is that process alone, with no window, and `--connect HOST:PORT` is the window alone, joining one that's already running. That one can be - Vamp, studio's headless groovebox: the window is one of its front panels. + Vamp, the apps repo's headless groovebox: the window is one of its front panels. The two SoundFont instruments, **SOUNDFONT** (General MIDI's 128 programs) and **SOUNDFONT DRUMS** (its kits), play whatever `.sf2` they find: the @@ -107,13 +109,13 @@ for the rest; the script says how many bars that changed. its track and hands it to the master worker (`app/engine/master.py`), which sums them, runs the master effects, and is the one stream the AudioWorklet (`workers/audio_worklet.js`) plays. -- The project is `songmodel.model`, in studio's `lib/songmodel/`: plain data, +- The project is `songmodel.model`, in the apps repo's `lib/songmodel/`: plain data, changed only by ops that both sides apply. Vamp plays the same songs. - `workers/` is pyscript-template's worker kit. `songmodel.model` imports on CPython, Pyodide and MicroPython. The engine and UI around it haven't been tried on a board yet; on a board, the songs play on -Vamp instead (`apps/vamp/`). +Vamp instead (`programs/vamp/`). ## Checks diff --git a/apps/ostinato/ROADMAP.md b/programs/ostinato/ROADMAP.md similarity index 100% rename from apps/ostinato/ROADMAP.md rename to programs/ostinato/ROADMAP.md diff --git a/apps/ostinato/app/boot_config.js b/programs/ostinato/app/boot_config.js similarity index 100% rename from apps/ostinato/app/boot_config.js rename to programs/ostinato/app/boot_config.js diff --git a/apps/ostinato/app/engine/engine.py b/programs/ostinato/app/engine/engine.py similarity index 100% rename from apps/ostinato/app/engine/engine.py rename to programs/ostinato/app/engine/engine.py diff --git a/apps/ostinato/app/engine/enginehost.py b/programs/ostinato/app/engine/enginehost.py similarity index 100% rename from apps/ostinato/app/engine/enginehost.py rename to programs/ostinato/app/engine/enginehost.py diff --git a/apps/ostinato/app/engine/main.py b/programs/ostinato/app/engine/main.py similarity index 100% rename from apps/ostinato/app/engine/main.py rename to programs/ostinato/app/engine/main.py diff --git a/apps/ostinato/app/engine/master.py b/programs/ostinato/app/engine/master.py similarity index 100% rename from apps/ostinato/app/engine/master.py rename to programs/ostinato/app/engine/master.py diff --git a/apps/ostinato/app/engine/mastercore.py b/programs/ostinato/app/engine/mastercore.py similarity index 100% rename from apps/ostinato/app/engine/mastercore.py rename to programs/ostinato/app/engine/mastercore.py diff --git a/apps/ostinato/app/precache.json b/programs/ostinato/app/precache.json similarity index 100% rename from apps/ostinato/app/precache.json rename to programs/ostinato/app/precache.json diff --git a/apps/ostinato/app/ui/catalog.py b/programs/ostinato/app/ui/catalog.py similarity index 100% rename from apps/ostinato/app/ui/catalog.py rename to programs/ostinato/app/ui/catalog.py diff --git a/apps/ostinato/app/ui/editor.py b/programs/ostinato/app/ui/editor.py similarity index 100% rename from apps/ostinato/app/ui/editor.py rename to programs/ostinato/app/ui/editor.py diff --git a/apps/ostinato/app/ui/engines.py b/programs/ostinato/app/ui/engines.py similarity index 100% rename from apps/ostinato/app/ui/engines.py rename to programs/ostinato/app/ui/engines.py diff --git a/apps/ostinato/app/ui/files.py b/programs/ostinato/app/ui/files.py similarity index 100% rename from apps/ostinato/app/ui/files.py rename to programs/ostinato/app/ui/files.py diff --git a/apps/ostinato/app/ui/fonts.py b/programs/ostinato/app/ui/fonts.py similarity index 100% rename from apps/ostinato/app/ui/fonts.py rename to programs/ostinato/app/ui/fonts.py diff --git a/apps/ostinato/app/ui/help.py b/programs/ostinato/app/ui/help.py similarity index 100% rename from apps/ostinato/app/ui/help.py rename to programs/ostinato/app/ui/help.py diff --git a/apps/ostinato/app/ui/history.py b/programs/ostinato/app/ui/history.py similarity index 100% rename from apps/ostinato/app/ui/history.py rename to programs/ostinato/app/ui/history.py diff --git a/apps/ostinato/app/ui/icons.py b/programs/ostinato/app/ui/icons.py similarity index 100% rename from apps/ostinato/app/ui/icons.py rename to programs/ostinato/app/ui/icons.py diff --git a/apps/ostinato/app/ui/inspector.py b/programs/ostinato/app/ui/inspector.py similarity index 100% rename from apps/ostinato/app/ui/inspector.py rename to programs/ostinato/app/ui/inspector.py diff --git a/apps/ostinato/app/ui/keyboard.py b/programs/ostinato/app/ui/keyboard.py similarity index 100% rename from apps/ostinato/app/ui/keyboard.py rename to programs/ostinato/app/ui/keyboard.py diff --git a/apps/ostinato/app/ui/knob.py b/programs/ostinato/app/ui/knob.py similarity index 100% rename from apps/ostinato/app/ui/knob.py rename to programs/ostinato/app/ui/knob.py diff --git a/apps/ostinato/app/ui/layout.py b/programs/ostinato/app/ui/layout.py similarity index 100% rename from apps/ostinato/app/ui/layout.py rename to programs/ostinato/app/ui/layout.py diff --git a/apps/ostinato/app/ui/main.py b/programs/ostinato/app/ui/main.py similarity index 100% rename from apps/ostinato/app/ui/main.py rename to programs/ostinato/app/ui/main.py diff --git a/apps/ostinato/app/ui/meter.py b/programs/ostinato/app/ui/meter.py similarity index 100% rename from apps/ostinato/app/ui/meter.py rename to programs/ostinato/app/ui/meter.py diff --git a/apps/ostinato/app/ui/page.py b/programs/ostinato/app/ui/page.py similarity index 100% rename from apps/ostinato/app/ui/page.py rename to programs/ostinato/app/ui/page.py diff --git a/apps/ostinato/app/ui/song.py b/programs/ostinato/app/ui/song.py similarity index 100% rename from apps/ostinato/app/ui/song.py rename to programs/ostinato/app/ui/song.py diff --git a/apps/ostinato/app/ui/status.py b/programs/ostinato/app/ui/status.py similarity index 100% rename from apps/ostinato/app/ui/status.py rename to programs/ostinato/app/ui/status.py diff --git a/apps/ostinato/app/ui/theme.py b/programs/ostinato/app/ui/theme.py similarity index 100% rename from apps/ostinato/app/ui/theme.py rename to programs/ostinato/app/ui/theme.py diff --git a/apps/ostinato/app/ui/tooltip.py b/programs/ostinato/app/ui/tooltip.py similarity index 100% rename from apps/ostinato/app/ui/tooltip.py rename to programs/ostinato/app/ui/tooltip.py diff --git a/apps/ostinato/app/ui/tracks.py b/programs/ostinato/app/ui/tracks.py similarity index 100% rename from apps/ostinato/app/ui/tracks.py rename to programs/ostinato/app/ui/tracks.py diff --git a/apps/ostinato/app/ui/transport.py b/programs/ostinato/app/ui/transport.py similarity index 100% rename from apps/ostinato/app/ui/transport.py rename to programs/ostinato/app/ui/transport.py diff --git a/apps/ostinato/assets/ICONS-LICENSE b/programs/ostinato/assets/ICONS-LICENSE similarity index 100% rename from apps/ostinato/assets/ICONS-LICENSE rename to programs/ostinato/assets/ICONS-LICENSE diff --git a/apps/ostinato/assets/catalog.json b/programs/ostinato/assets/catalog.json similarity index 100% rename from apps/ostinato/assets/catalog.json rename to programs/ostinato/assets/catalog.json diff --git a/apps/ostinato/assets/demo_song.json b/programs/ostinato/assets/demo_song.json similarity index 100% rename from apps/ostinato/assets/demo_song.json rename to programs/ostinato/assets/demo_song.json diff --git a/apps/ostinato/assets/icons_20.bin b/programs/ostinato/assets/icons_20.bin similarity index 100% rename from apps/ostinato/assets/icons_20.bin rename to programs/ostinato/assets/icons_20.bin diff --git a/apps/ostinato/assets/icons_24.bin b/programs/ostinato/assets/icons_24.bin similarity index 100% rename from apps/ostinato/assets/icons_24.bin rename to programs/ostinato/assets/icons_24.bin diff --git a/apps/ostinato/assets/icons_32.bin b/programs/ostinato/assets/icons_32.bin similarity index 100% rename from apps/ostinato/assets/icons_32.bin rename to programs/ostinato/assets/icons_32.bin diff --git a/apps/ostinato/assets/icons_48.bin b/programs/ostinato/assets/icons_48.bin similarity index 100% rename from apps/ostinato/assets/icons_48.bin rename to programs/ostinato/assets/icons_48.bin diff --git a/apps/ostinato/desktop/app.py b/programs/ostinato/desktop/app.py similarity index 100% rename from apps/ostinato/desktop/app.py rename to programs/ostinato/desktop/app.py diff --git a/apps/ostinato/desktop/control.py b/programs/ostinato/desktop/control.py similarity index 100% rename from apps/ostinato/desktop/control.py rename to programs/ostinato/desktop/control.py diff --git a/apps/ostinato/desktop/device.py b/programs/ostinato/desktop/device.py similarity index 98% rename from apps/ostinato/desktop/device.py rename to programs/ostinato/desktop/device.py index a8b87e8..0801e07 100644 --- a/apps/ostinato/desktop/device.py +++ b/programs/ostinato/desktop/device.py @@ -7,10 +7,10 @@ python -m ostinato --role device [--port 9100] [--song FILE] It runs the engines and the mixer (desktop/runners.py) and holds the song -(songmodel.link, in studio's lib/). Panels connect on one port: the desktop UI +(songmodel.link, in the apps repo's lib/). Panels connect on one port: the desktop UI (``python -m ostinato --connect 127.0.0.1:9100``, or ``--split``, which starts this process for you) and any browser, which gets the song page -(songmodel's page.html, in studio's lib/) at http://:9100/. +(songmodel's page.html, in the apps repo's lib/) at http://:9100/. Every edit is a JSON Patch op a panel sends. The device numbers it, applies it to the song and to the engines, and sends it to every panel. The engines' diff --git a/apps/ostinato/desktop/dialogs.py b/programs/ostinato/desktop/dialogs.py similarity index 100% rename from apps/ostinato/desktop/dialogs.py rename to programs/ostinato/desktop/dialogs.py diff --git a/apps/ostinato/desktop/engine_loop.py b/programs/ostinato/desktop/engine_loop.py similarity index 100% rename from apps/ostinato/desktop/engine_loop.py rename to programs/ostinato/desktop/engine_loop.py diff --git a/apps/ostinato/desktop/linkrunner.py b/programs/ostinato/desktop/linkrunner.py similarity index 100% rename from apps/ostinato/desktop/linkrunner.py rename to programs/ostinato/desktop/linkrunner.py diff --git a/apps/ostinato/desktop/midi.py b/programs/ostinato/desktop/midi.py similarity index 100% rename from apps/ostinato/desktop/midi.py rename to programs/ostinato/desktop/midi.py diff --git a/apps/ostinato/desktop/midi_send.py b/programs/ostinato/desktop/midi_send.py similarity index 100% rename from apps/ostinato/desktop/midi_send.py rename to programs/ostinato/desktop/midi_send.py diff --git a/apps/ostinato/desktop/mixer.py b/programs/ostinato/desktop/mixer.py similarity index 100% rename from apps/ostinato/desktop/mixer.py rename to programs/ostinato/desktop/mixer.py diff --git a/apps/ostinato/desktop/patch_synthio.py b/programs/ostinato/desktop/patch_synthio.py similarity index 100% rename from apps/ostinato/desktop/patch_synthio.py rename to programs/ostinato/desktop/patch_synthio.py diff --git a/apps/ostinato/desktop/ring.py b/programs/ostinato/desktop/ring.py similarity index 100% rename from apps/ostinato/desktop/ring.py rename to programs/ostinato/desktop/ring.py diff --git a/apps/ostinato/desktop/run.sh b/programs/ostinato/desktop/run.sh similarity index 100% rename from apps/ostinato/desktop/run.sh rename to programs/ostinato/desktop/run.sh diff --git a/apps/ostinato/desktop/runners.py b/programs/ostinato/desktop/runners.py similarity index 100% rename from apps/ostinato/desktop/runners.py rename to programs/ostinato/desktop/runners.py diff --git a/apps/ostinato/desktop/send.py b/programs/ostinato/desktop/send.py similarity index 100% rename from apps/ostinato/desktop/send.py rename to programs/ostinato/desktop/send.py diff --git a/apps/ostinato/desktop/stage.sh b/programs/ostinato/desktop/stage.sh similarity index 100% rename from apps/ostinato/desktop/stage.sh rename to programs/ostinato/desktop/stage.sh diff --git a/apps/ostinato/desktop/stress.py b/programs/ostinato/desktop/stress.py similarity index 100% rename from apps/ostinato/desktop/stress.py rename to programs/ostinato/desktop/stress.py diff --git a/apps/ostinato/desktop/ui/engines.py b/programs/ostinato/desktop/ui/engines.py similarity index 100% rename from apps/ostinato/desktop/ui/engines.py rename to programs/ostinato/desktop/ui/engines.py diff --git a/apps/ostinato/desktop/ui/page.py b/programs/ostinato/desktop/ui/page.py similarity index 100% rename from apps/ostinato/desktop/ui/page.py rename to programs/ostinato/desktop/ui/page.py diff --git a/apps/ostinato/desktop/window.py b/programs/ostinato/desktop/window.py similarity index 100% rename from apps/ostinato/desktop/window.py rename to programs/ostinato/desktop/window.py diff --git a/apps/ostinato/dmk25.json b/programs/ostinato/dmk25.json similarity index 100% rename from apps/ostinato/dmk25.json rename to programs/ostinato/dmk25.json diff --git a/apps/ostinato/engine/engine.py b/programs/ostinato/engine/engine.py similarity index 100% rename from apps/ostinato/engine/engine.py rename to programs/ostinato/engine/engine.py diff --git a/apps/ostinato/engine/worker.js b/programs/ostinato/engine/worker.js similarity index 100% rename from apps/ostinato/engine/worker.js rename to programs/ostinato/engine/worker.js diff --git a/apps/ostinato/engine/worklet.js b/programs/ostinato/engine/worklet.js similarity index 100% rename from apps/ostinato/engine/worklet.js rename to programs/ostinato/engine/worklet.js diff --git a/apps/ostinato/icon-192.png b/programs/ostinato/icon-192.png similarity index 100% rename from apps/ostinato/icon-192.png rename to programs/ostinato/icon-192.png diff --git a/apps/ostinato/icon-512.png b/programs/ostinato/icon-512.png similarity index 100% rename from apps/ostinato/icon-512.png rename to programs/ostinato/icon-512.png diff --git a/apps/ostinato/index.html b/programs/ostinato/index.html similarity index 100% rename from apps/ostinato/index.html rename to programs/ostinato/index.html diff --git a/apps/ostinato/manifest.json b/programs/ostinato/manifest.json similarity index 100% rename from apps/ostinato/manifest.json rename to programs/ostinato/manifest.json diff --git a/apps/ostinato/ostinato/__init__.py b/programs/ostinato/ostinato/__init__.py similarity index 89% rename from apps/ostinato/ostinato/__init__.py rename to programs/ostinato/ostinato/__init__.py index a231eae..b35c56c 100644 --- a/apps/ostinato/ostinato/__init__.py +++ b/programs/ostinato/ostinato/__init__.py @@ -4,7 +4,7 @@ """Ostinato on the desktop: ``python -m ostinato`` from a checkout. -The browser version and the desktop app share everything in ``app/`` and studio's ``songmodel``. What +The browser version and the desktop app share everything in ``app/`` and the apps repo's ``songmodel``. What differs lives in ``desktop/``: the runner that hosts the engines (processes or threads), the mixer that plays them through WASAPI, Windows MIDI, and the page (dialogs, autosave, shortcuts, the control socket the checks use). @@ -16,7 +16,7 @@ import sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -# studio's lib/ holds what Ostinato shares with Vamp (songmodel: the song and +# The apps repo's lib/ holds what Ostinato shares with Vamp (songmodel: the song and # its link). From a checkout it's two folders up; a staged copy # (tools/win_stage.sh) carries it in lib/ beside the rest. LIB = os.path.join(ROOT, "lib") diff --git a/apps/ostinato/ostinato/__main__.py b/programs/ostinato/ostinato/__main__.py similarity index 100% rename from apps/ostinato/ostinato/__main__.py rename to programs/ostinato/ostinato/__main__.py diff --git a/apps/ostinato/scripts/fetch_midi.py b/programs/ostinato/scripts/fetch_midi.py similarity index 100% rename from apps/ostinato/scripts/fetch_midi.py rename to programs/ostinato/scripts/fetch_midi.py diff --git a/apps/ostinato/scripts/fetch_wheels.py b/programs/ostinato/scripts/fetch_wheels.py similarity index 100% rename from apps/ostinato/scripts/fetch_wheels.py rename to programs/ostinato/scripts/fetch_wheels.py diff --git a/apps/ostinato/scripts/make_app_icon.py b/programs/ostinato/scripts/make_app_icon.py similarity index 100% rename from apps/ostinato/scripts/make_app_icon.py rename to programs/ostinato/scripts/make_app_icon.py diff --git a/apps/ostinato/scripts/make_catalog.mjs b/programs/ostinato/scripts/make_catalog.mjs similarity index 100% rename from apps/ostinato/scripts/make_catalog.mjs rename to programs/ostinato/scripts/make_catalog.mjs diff --git a/apps/ostinato/scripts/make_icon_font.py b/programs/ostinato/scripts/make_icon_font.py similarity index 100% rename from apps/ostinato/scripts/make_icon_font.py rename to programs/ostinato/scripts/make_icon_font.py diff --git a/apps/ostinato/scripts/make_soundfont_catalog.py b/programs/ostinato/scripts/make_soundfont_catalog.py similarity index 100% rename from apps/ostinato/scripts/make_soundfont_catalog.py rename to programs/ostinato/scripts/make_soundfont_catalog.py diff --git a/apps/ostinato/scripts/soundhelix.sh b/programs/ostinato/scripts/soundhelix.sh similarity index 100% rename from apps/ostinato/scripts/soundhelix.sh rename to programs/ostinato/scripts/soundhelix.sh diff --git a/apps/ostinato/scripts/soundhelix/Export.java b/programs/ostinato/scripts/soundhelix/Export.java similarity index 100% rename from apps/ostinato/scripts/soundhelix/Export.java rename to programs/ostinato/scripts/soundhelix/Export.java diff --git a/apps/ostinato/scripts/soundhelix_project.py b/programs/ostinato/scripts/soundhelix_project.py similarity index 100% rename from apps/ostinato/scripts/soundhelix_project.py rename to programs/ostinato/scripts/soundhelix_project.py diff --git a/apps/ostinato/scripts/vendor_pyscript.sh b/programs/ostinato/scripts/vendor_pyscript.sh similarity index 100% rename from apps/ostinato/scripts/vendor_pyscript.sh rename to programs/ostinato/scripts/vendor_pyscript.sh diff --git a/apps/ostinato/songs/sketches/low_sun.json b/programs/ostinato/songs/sketches/low_sun.json similarity index 100% rename from apps/ostinato/songs/sketches/low_sun.json rename to programs/ostinato/songs/sketches/low_sun.json diff --git a/apps/ostinato/songs/sketches/lowrey_field.json b/programs/ostinato/songs/sketches/lowrey_field.json similarity index 100% rename from apps/ostinato/songs/sketches/lowrey_field.json rename to programs/ostinato/songs/sketches/lowrey_field.json diff --git a/apps/ostinato/songs/sketches/make_sketches.py b/programs/ostinato/songs/sketches/make_sketches.py similarity index 100% rename from apps/ostinato/songs/sketches/make_sketches.py rename to programs/ostinato/songs/sketches/make_sketches.py diff --git a/apps/ostinato/songs/sketches/make_soundfont_check.py b/programs/ostinato/songs/sketches/make_soundfont_check.py similarity index 100% rename from apps/ostinato/songs/sketches/make_soundfont_check.py rename to programs/ostinato/songs/sketches/make_soundfont_check.py diff --git a/apps/ostinato/songs/sketches/soundfont_check.json b/programs/ostinato/songs/sketches/soundfont_check.json similarity index 100% rename from apps/ostinato/songs/sketches/soundfont_check.json rename to programs/ostinato/songs/sketches/soundfont_check.json diff --git a/apps/ostinato/songs/sketches/strange_days_out.json b/programs/ostinato/songs/sketches/strange_days_out.json similarity index 100% rename from apps/ostinato/songs/sketches/strange_days_out.json rename to programs/ostinato/songs/sketches/strange_days_out.json diff --git a/apps/ostinato/spike/host.js b/programs/ostinato/spike/host.js similarity index 100% rename from apps/ostinato/spike/host.js rename to programs/ostinato/spike/host.js diff --git a/apps/ostinato/spike/index.html b/programs/ostinato/spike/index.html similarity index 100% rename from apps/ostinato/spike/index.html rename to programs/ostinato/spike/index.html diff --git a/apps/ostinato/spike/kit_config.js b/programs/ostinato/spike/kit_config.js similarity index 100% rename from apps/ostinato/spike/kit_config.js rename to programs/ostinato/spike/kit_config.js diff --git a/apps/ostinato/spike/kit_test.html b/programs/ostinato/spike/kit_test.html similarity index 100% rename from apps/ostinato/spike/kit_test.html rename to programs/ostinato/spike/kit_test.html diff --git a/apps/ostinato/spike/kit_ui.py b/programs/ostinato/spike/kit_ui.py similarity index 100% rename from apps/ostinato/spike/kit_ui.py rename to programs/ostinato/spike/kit_ui.py diff --git a/apps/ostinato/spike/page.py b/programs/ostinato/spike/page.py similarity index 100% rename from apps/ostinato/spike/page.py rename to programs/ostinato/spike/page.py diff --git a/apps/ostinato/spike/pyscript.json b/programs/ostinato/spike/pyscript.json similarity index 100% rename from apps/ostinato/spike/pyscript.json rename to programs/ostinato/spike/pyscript.json diff --git a/apps/ostinato/spike/spike_config.js b/programs/ostinato/spike/spike_config.js similarity index 100% rename from apps/ostinato/spike/spike_config.js rename to programs/ostinato/spike/spike_config.js diff --git a/apps/ostinato/spike/ui_worker.html b/programs/ostinato/spike/ui_worker.html similarity index 100% rename from apps/ostinato/spike/ui_worker.html rename to programs/ostinato/spike/ui_worker.html diff --git a/apps/ostinato/spike/ui_worker.js b/programs/ostinato/spike/ui_worker.js similarity index 100% rename from apps/ostinato/spike/ui_worker.js rename to programs/ostinato/spike/ui_worker.js diff --git a/apps/ostinato/spike/ui_worker.py b/programs/ostinato/spike/ui_worker.py similarity index 100% rename from apps/ostinato/spike/ui_worker.py rename to programs/ostinato/spike/ui_worker.py diff --git a/apps/ostinato/sw.js b/programs/ostinato/sw.js similarity index 100% rename from apps/ostinato/sw.js rename to programs/ostinato/sw.js diff --git a/apps/ostinato/tests/test_template.py b/programs/ostinato/tests/test_template.py similarity index 98% rename from apps/ostinato/tests/test_template.py rename to programs/ostinato/tests/test_template.py index 0a761f8..91bd9c3 100644 --- a/apps/ostinato/tests/test_template.py +++ b/programs/ostinato/tests/test_template.py @@ -62,7 +62,7 @@ def test_manifest_is_valid_json(self): def test_worker_files_exist(self): """Every Python file app/boot_config.js hands a worker is in the tree - (vendored ones aside). lib/ is studio's lib/, two folders up, as + (vendored ones aside). lib/ is the apps repo's lib/, two folders up, as tools/serve.py serves it: the song model Ostinato shares with Vamp.""" cfg = json.loads(_read("app/boot_config.js").split("export default", 1)[1].rstrip().rstrip(";")) for worker in ("ui", "engine"): diff --git a/apps/ostinato/tools/app_play.py b/programs/ostinato/tools/app_play.py similarity index 100% rename from apps/ostinato/tools/app_play.py rename to programs/ostinato/tools/app_play.py diff --git a/apps/ostinato/tools/check_all.py b/programs/ostinato/tools/check_all.py similarity index 100% rename from apps/ostinato/tools/check_all.py rename to programs/ostinato/tools/check_all.py diff --git a/apps/ostinato/tools/check_no_links.py b/programs/ostinato/tools/check_no_links.py similarity index 100% rename from apps/ostinato/tools/check_no_links.py rename to programs/ostinato/tools/check_no_links.py diff --git a/apps/ostinato/tools/checkkit.py b/programs/ostinato/tools/checkkit.py similarity index 100% rename from apps/ostinato/tools/checkkit.py rename to programs/ostinato/tools/checkkit.py diff --git a/apps/ostinato/tools/click_check.py b/programs/ostinato/tools/click_check.py similarity index 100% rename from apps/ostinato/tools/click_check.py rename to programs/ostinato/tools/click_check.py diff --git a/apps/ostinato/tools/demo_check.py b/programs/ostinato/tools/demo_check.py similarity index 100% rename from apps/ostinato/tools/demo_check.py rename to programs/ostinato/tools/demo_check.py diff --git a/apps/ostinato/tools/desktop_demo_check.py b/programs/ostinato/tools/desktop_demo_check.py similarity index 100% rename from apps/ostinato/tools/desktop_demo_check.py rename to programs/ostinato/tools/desktop_demo_check.py diff --git a/apps/ostinato/tools/desktop_input_check.py b/programs/ostinato/tools/desktop_input_check.py similarity index 100% rename from apps/ostinato/tools/desktop_input_check.py rename to programs/ostinato/tools/desktop_input_check.py diff --git a/apps/ostinato/tools/desktop_offline_check.py b/programs/ostinato/tools/desktop_offline_check.py similarity index 100% rename from apps/ostinato/tools/desktop_offline_check.py rename to programs/ostinato/tools/desktop_offline_check.py diff --git a/apps/ostinato/tools/desktop_tap_check.py b/programs/ostinato/tools/desktop_tap_check.py similarity index 100% rename from apps/ostinato/tools/desktop_tap_check.py rename to programs/ostinato/tools/desktop_tap_check.py diff --git a/apps/ostinato/tools/desktopkit.py b/programs/ostinato/tools/desktopkit.py similarity index 100% rename from apps/ostinato/tools/desktopkit.py rename to programs/ostinato/tools/desktopkit.py diff --git a/apps/ostinato/tools/effect_sweep.mjs b/programs/ostinato/tools/effect_sweep.mjs similarity index 97% rename from apps/ostinato/tools/effect_sweep.mjs rename to programs/ostinato/tools/effect_sweep.mjs index ac64bd4..8e3b30f 100644 --- a/apps/ostinato/tools/effect_sweep.mjs +++ b/programs/ostinato/tools/effect_sweep.mjs @@ -8,7 +8,7 @@ const R = new URL("../", import.meta.url).pathname; const w = JSON.parse(fs.readFileSync(R + "wheels.json")); const py = await loadPyodide({ indexURL: R + "vendor/pyscript/pyodide/" }); for (const n of ["numpy", "omnipython-audiodsp", "omnipython-audioeffects", "omnipython-audioinstruments"]) await py.loadPackage(R + "vendor/wheels/" + w[n].file); -// songmodel (the song model) is in studio's lib/, two folders up. +// songmodel (the song model) is in the apps repo's lib/, two folders up. py.FS.mkdirTree("/home/pyodide/songmodel"); for (const f of ["__init__.py", "model.py"]) py.FS.writeFile("/home/pyodide/songmodel/" + f, fs.readFileSync(R + "../../lib/songmodel/" + f)); py.FS.writeFile("/home/pyodide/engine.py", fs.readFileSync(R + "app/engine/engine.py")); diff --git a/apps/ostinato/tools/engine_bench.mjs b/programs/ostinato/tools/engine_bench.mjs similarity index 95% rename from apps/ostinato/tools/engine_bench.mjs rename to programs/ostinato/tools/engine_bench.mjs index d33f829..d3d8699 100644 --- a/apps/ostinato/tools/engine_bench.mjs +++ b/programs/ostinato/tools/engine_bench.mjs @@ -7,7 +7,7 @@ const R = new URL("../", import.meta.url).pathname; const w = JSON.parse(fs.readFileSync(R + "wheels.json")); const py = await loadPyodide({ indexURL: R + "vendor/pyscript/pyodide/" }); for (const n of ["numpy","omnipython-audiodsp","omnipython-audioeffects","omnipython-audioinstruments"]) await py.loadPackage(R + "vendor/wheels/" + w[n].file); -// songmodel (the song model) is in studio's lib/, two folders up. +// songmodel (the song model) is in the apps repo's lib/, two folders up. py.FS.mkdirTree("/home/pyodide/songmodel"); for (const f of ["__init__.py", "model.py"]) py.FS.writeFile("/home/pyodide/songmodel/" + f, fs.readFileSync(R + "../../lib/songmodel/" + f)); py.FS.writeFile("/home/pyodide/engine.py", fs.readFileSync(R + "app/engine/engine.py")); diff --git a/apps/ostinato/tools/fake_pickers.js b/programs/ostinato/tools/fake_pickers.js similarity index 100% rename from apps/ostinato/tools/fake_pickers.js rename to programs/ostinato/tools/fake_pickers.js diff --git a/apps/ostinato/tools/fake_webmidi.js b/programs/ostinato/tools/fake_webmidi.js similarity index 100% rename from apps/ostinato/tools/fake_webmidi.js rename to programs/ostinato/tools/fake_webmidi.js diff --git a/apps/ostinato/tools/files_check.py b/programs/ostinato/tools/files_check.py similarity index 100% rename from apps/ostinato/tools/files_check.py rename to programs/ostinato/tools/files_check.py diff --git a/apps/ostinato/tools/gen_boot.py b/programs/ostinato/tools/gen_boot.py similarity index 95% rename from apps/ostinato/tools/gen_boot.py rename to programs/ostinato/tools/gen_boot.py index f18e571..cb29f31 100644 --- a/apps/ostinato/tools/gen_boot.py +++ b/programs/ostinato/tools/gen_boot.py @@ -20,9 +20,9 @@ # The desktop app's MIDI port and its link between processes (hmilink, and # the song as its model): scripts/fetch_midi.py fetches them beside the rest. DESKTOP_ONLY = ("vendor/lib/midiports/windows.py", "vendor/lib/hmilink/", "lib/songmodel/link.py") -# songmodel (the song model Ostinato shares with Vamp) is in studio's lib/, two +# songmodel (the song model Ostinato shares with Vamp) is in the apps repo's lib/, two # folders up. The page fetches it as lib/songmodel/...: tools/serve.py serves -# studio's lib/ there, and a published copy carries it at that path. +# The apps repo's lib/ there, and a published copy carries it at that path. LIB = ROOT.parent.parent / "lib" diff --git a/apps/ostinato/tools/gen_spike_config.py b/programs/ostinato/tools/gen_spike_config.py similarity index 100% rename from apps/ostinato/tools/gen_spike_config.py rename to programs/ostinato/tools/gen_spike_config.py diff --git a/apps/ostinato/tools/groovebox_check.py b/programs/ostinato/tools/groovebox_check.py similarity index 100% rename from apps/ostinato/tools/groovebox_check.py rename to programs/ostinato/tools/groovebox_check.py diff --git a/apps/ostinato/tools/instrument_sweep.mjs b/programs/ostinato/tools/instrument_sweep.mjs similarity index 98% rename from apps/ostinato/tools/instrument_sweep.mjs rename to programs/ostinato/tools/instrument_sweep.mjs index 200eab5..46ab00f 100644 --- a/apps/ostinato/tools/instrument_sweep.mjs +++ b/programs/ostinato/tools/instrument_sweep.mjs @@ -11,7 +11,7 @@ const R = new URL("../", import.meta.url).pathname; const w = JSON.parse(fs.readFileSync(R + "wheels.json")); const py = await loadPyodide({ indexURL: R + "vendor/pyscript/pyodide/" }); for (const n of ["numpy", "omnipython-audiodsp", "omnipython-audioeffects", "omnipython-audioinstruments"]) await py.loadPackage(R + "vendor/wheels/" + w[n].file); -// songmodel (the song model) is in studio's lib/, two folders up. +// songmodel (the song model) is in the apps repo's lib/, two folders up. py.FS.mkdirTree("/home/pyodide/songmodel"); for (const f of ["__init__.py", "model.py"]) py.FS.writeFile("/home/pyodide/songmodel/" + f, fs.readFileSync(R + "../../lib/songmodel/" + f)); py.FS.writeFile("/home/pyodide/engine.py", fs.readFileSync(R + "app/engine/engine.py")); diff --git a/apps/ostinato/tools/instrument_sweep_check.py b/programs/ostinato/tools/instrument_sweep_check.py similarity index 100% rename from apps/ostinato/tools/instrument_sweep_check.py rename to programs/ostinato/tools/instrument_sweep_check.py diff --git a/apps/ostinato/tools/knob_check.py b/programs/ostinato/tools/knob_check.py similarity index 100% rename from apps/ostinato/tools/knob_check.py rename to programs/ostinato/tools/knob_check.py diff --git a/apps/ostinato/tools/link_check.py b/programs/ostinato/tools/link_check.py similarity index 100% rename from apps/ostinato/tools/link_check.py rename to programs/ostinato/tools/link_check.py diff --git a/apps/ostinato/tools/lookahead_check.py b/programs/ostinato/tools/lookahead_check.py similarity index 100% rename from apps/ostinato/tools/lookahead_check.py rename to programs/ostinato/tools/lookahead_check.py diff --git a/apps/ostinato/tools/loopback.sh b/programs/ostinato/tools/loopback.sh similarity index 100% rename from apps/ostinato/tools/loopback.sh rename to programs/ostinato/tools/loopback.sh diff --git a/apps/ostinato/tools/master_check.py b/programs/ostinato/tools/master_check.py similarity index 100% rename from apps/ostinato/tools/master_check.py rename to programs/ostinato/tools/master_check.py diff --git a/apps/ostinato/tools/midi_play.py b/programs/ostinato/tools/midi_play.py similarity index 100% rename from apps/ostinato/tools/midi_play.py rename to programs/ostinato/tools/midi_play.py diff --git a/apps/ostinato/tools/offline_check.py b/programs/ostinato/tools/offline_check.py similarity index 100% rename from apps/ostinato/tools/offline_check.py rename to programs/ostinato/tools/offline_check.py diff --git a/apps/ostinato/tools/scenarios/canon_in_d.yaml b/programs/ostinato/tools/scenarios/canon_in_d.yaml similarity index 100% rename from apps/ostinato/tools/scenarios/canon_in_d.yaml rename to programs/ostinato/tools/scenarios/canon_in_d.yaml diff --git a/apps/ostinato/tools/serve.py b/programs/ostinato/tools/serve.py similarity index 97% rename from apps/ostinato/tools/serve.py rename to programs/ostinato/tools/serve.py index 3023ccb..4c06f59 100644 --- a/apps/ostinato/tools/serve.py +++ b/programs/ostinato/tools/serve.py @@ -6,7 +6,7 @@ .venv/bin/python tools/serve.py [--port 8765] [--isolate] -No caching, so an edit shows on reload. ``/lib/`` is studio's ``lib/``, two +No caching, so an edit shows on reload. ``/lib/`` is the apps repo's ``lib/``, two folders up, where the song model Ostinato shares with Vamp lives (songmodel). ``--isolate`` adds the COOP/COEP headers that SharedArrayBuffer needs (P0's SAB comparison only). """ diff --git a/apps/ostinato/tools/sketch_render.py b/programs/ostinato/tools/sketch_render.py similarity index 100% rename from apps/ostinato/tools/sketch_render.py rename to programs/ostinato/tools/sketch_render.py diff --git a/apps/ostinato/tools/soundfont_check.py b/programs/ostinato/tools/soundfont_check.py similarity index 100% rename from apps/ostinato/tools/soundfont_check.py rename to programs/ostinato/tools/soundfont_check.py diff --git a/apps/ostinato/tools/split_check.py b/programs/ostinato/tools/split_check.py similarity index 100% rename from apps/ostinato/tools/split_check.py rename to programs/ostinato/tools/split_check.py diff --git a/apps/ostinato/tools/switch_check.py b/programs/ostinato/tools/switch_check.py similarity index 100% rename from apps/ostinato/tools/switch_check.py rename to programs/ostinato/tools/switch_check.py diff --git a/apps/ostinato/tools/tap_check.py b/programs/ostinato/tools/tap_check.py similarity index 100% rename from apps/ostinato/tools/tap_check.py rename to programs/ostinato/tools/tap_check.py diff --git a/apps/ostinato/tools/timing_check.py b/programs/ostinato/tools/timing_check.py similarity index 100% rename from apps/ostinato/tools/timing_check.py rename to programs/ostinato/tools/timing_check.py diff --git a/apps/ostinato/tools/transport_check.py b/programs/ostinato/tools/transport_check.py similarity index 100% rename from apps/ostinato/tools/transport_check.py rename to programs/ostinato/tools/transport_check.py diff --git a/apps/ostinato/tools/win_chrome.sh b/programs/ostinato/tools/win_chrome.sh similarity index 100% rename from apps/ostinato/tools/win_chrome.sh rename to programs/ostinato/tools/win_chrome.sh diff --git a/apps/ostinato/tools/win_kill.sh b/programs/ostinato/tools/win_kill.sh similarity index 100% rename from apps/ostinato/tools/win_kill.sh rename to programs/ostinato/tools/win_kill.sh diff --git a/apps/ostinato/tools/win_run.sh b/programs/ostinato/tools/win_run.sh similarity index 100% rename from apps/ostinato/tools/win_run.sh rename to programs/ostinato/tools/win_run.sh diff --git a/apps/ostinato/tools/win_send.py b/programs/ostinato/tools/win_send.py similarity index 100% rename from apps/ostinato/tools/win_send.py rename to programs/ostinato/tools/win_send.py diff --git a/apps/ostinato/tools/win_stage.sh b/programs/ostinato/tools/win_stage.sh similarity index 91% rename from apps/ostinato/tools/win_stage.sh rename to programs/ostinato/tools/win_stage.sh index 20d22cf..3ecd4ec 100755 --- a/apps/ostinato/tools/win_stage.sh +++ b/programs/ostinato/tools/win_stage.sh @@ -14,7 +14,7 @@ for d in app desktop ostinato assets songs; do done mkdir -p "$DEST/vendor" rsync -a --delete --exclude __pycache__ "$ROOT/vendor/lib/" "$DEST/vendor/lib/" -# songmodel, which Ostinato shares with Vamp, lives in studio's lib/. +# songmodel, which Ostinato shares with Vamp, lives in the apps repo's lib/. mkdir -p "$DEST/lib" rsync -a --delete --exclude __pycache__ "$ROOT/../../lib/songmodel/" "$DEST/lib/songmodel/" echo "staged in $DEST" diff --git a/apps/ostinato/tools/workflow2_check.py b/programs/ostinato/tools/workflow2_check.py similarity index 100% rename from apps/ostinato/tools/workflow2_check.py rename to programs/ostinato/tools/workflow2_check.py diff --git a/apps/ostinato/tools/workflow3_check.py b/programs/ostinato/tools/workflow3_check.py similarity index 100% rename from apps/ostinato/tools/workflow3_check.py rename to programs/ostinato/tools/workflow3_check.py diff --git a/apps/ostinato/tools/workflow_check.py b/programs/ostinato/tools/workflow_check.py similarity index 100% rename from apps/ostinato/tools/workflow_check.py rename to programs/ostinato/tools/workflow_check.py diff --git a/apps/ostinato/wheels.json b/programs/ostinato/wheels.json similarity index 100% rename from apps/ostinato/wheels.json rename to programs/ostinato/wheels.json diff --git a/apps/ostinato/workers/audio_worklet.js b/programs/ostinato/workers/audio_worklet.js similarity index 100% rename from apps/ostinato/workers/audio_worklet.js rename to programs/ostinato/workers/audio_worklet.js diff --git a/apps/ostinato/workers/host.js b/programs/ostinato/workers/host.js similarity index 100% rename from apps/ostinato/workers/host.js rename to programs/ostinato/workers/host.js diff --git a/apps/ostinato/workers/python_worker.js b/programs/ostinato/workers/python_worker.js similarity index 100% rename from apps/ostinato/workers/python_worker.js rename to programs/ostinato/workers/python_worker.js diff --git a/programs/vamp/README.md b/programs/vamp/README.md new file mode 100644 index 0000000..c15ec3e --- /dev/null +++ b/programs/vamp/README.md @@ -0,0 +1,11 @@ +# Vamp + +Vamp plays Ostinato's songs: the song and the sound live on one MicroPython, +and the screens are somewhere else. Ostinato's window and a browser page edit +the song it plays, live, over the network, and a MIDI keyboard on the box plays +the selected track. + +Runs on: an ESP32-P4 board, or desktop MicroPython on Linux or Windows. It needs +a General MIDI SoundFont. [Put Vamp on a board](../../docs/guides/put-vamp-on-a-board.md) +has the steps for both, and the options are listed at the top of +[`main.py`](main.py). diff --git a/apps/vamp/__init__.py b/programs/vamp/__init__.py similarity index 100% rename from apps/vamp/__init__.py rename to programs/vamp/__init__.py diff --git a/apps/vamp/device.py b/programs/vamp/device.py similarity index 99% rename from apps/vamp/device.py rename to programs/vamp/device.py index 1388904..adc1ee8 100644 --- a/apps/vamp/device.py +++ b/programs/vamp/device.py @@ -5,7 +5,7 @@ """Vamp as an hmilink device: it holds the song and makes the sound, in one MicroPython process. -Ostinato's split device (apps/ostinato/desktop/device.py) runs engines in +Ostinato's split device (programs/ostinato/desktop/device.py) runs engines in processes of their own and passes messages; this one has one engine (`vamp.engine`) and calls it. To a panel the two look the same: Ostinato's desktop window (``python -m ostinato --connect BOX:9100``) and the browser page the box serves at diff --git a/apps/vamp/engine.py b/programs/vamp/engine.py similarity index 99% rename from apps/vamp/engine.py rename to programs/vamp/engine.py index 9ce4071..5b3454b 100644 --- a/apps/vamp/engine.py +++ b/programs/vamp/engine.py @@ -6,7 +6,7 @@ This is a groovebox's engine on a board (an ESP32-P4) and on desktop MicroPython, one process with no workers. Ostinato's engine -(apps/ostinato/app/engine/engine.py) renders each track in Python, a block at +(programs/ostinato/app/engine/engine.py) renders each track in Python, a block at a time; here nothing in Python touches the audio. The tracks share one ``soundfont.Voices`` pool, the pool goes through the master rack and a level, and the audio pump renders all of it on the other core. diff --git a/apps/vamp/main.py b/programs/vamp/main.py similarity index 98% rename from apps/vamp/main.py rename to programs/vamp/main.py index cae3e2f..853e8ff 100644 --- a/apps/vamp/main.py +++ b/programs/vamp/main.py @@ -8,8 +8,8 @@ import vamp; vamp.run() -On desktop MicroPython (unix or Windows), with studio's ``lib/`` and hmilink on -MICROPYPATH:: +On desktop MicroPython (unix or Windows), with this repository's ``programs/`` and ``lib/`` and +hmilink on MICROPYPATH:: micropython -X heapsize=96M -m vamp.main --bank GeneralUser-GS.sf2 [--song FILE] [--port 9100] [--midi NAME] [--lookahead-ms 750] [--seconds N] diff --git a/tests/test_folders.py b/tests/test_folders.py new file mode 100644 index 0000000..52a80dd --- /dev/null +++ b/tests/test_folders.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2026 PyDevices +# +# SPDX-License-Identifier: MIT +"""The two top-level folders keep their promises: every folder in ``apps/`` has +an ``app.json``, and every folder in ``programs/`` has a README with a +``Runs on`` line. + + python -m unittest tests.test_folders +""" + +import os +import unittest + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +def _folders(name): + top = os.path.join(ROOT, name) + return sorted( + d for d in os.listdir(top) + if os.path.isdir(os.path.join(top, d)) and d != "__pycache__" and not d.startswith(".") + ) + + +class FoldersTest(unittest.TestCase): + def test_every_app_has_an_app_json(self): + missing = [d for d in _folders("apps") if not os.path.isfile(os.path.join(ROOT, "apps", d, "app.json"))] + self.assertEqual(missing, [], "apps/ holds Home apps only; a program goes in programs/") + + def test_every_program_has_a_readme_with_runs_on(self): + bad = [] + for d in _folders("programs"): + path = os.path.join(ROOT, "programs", d, "README.md") + if not os.path.isfile(path): + bad.append(d + " (no README.md)") + continue + with open(path) as f: + if "runs on" not in f.read().lower(): + bad.append(d + " (no Runs on line)") + self.assertEqual(bad, []) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_relative_imports.py b/tests/test_relative_imports.py index 59f499a..96d0dce 100644 --- a/tests/test_relative_imports.py +++ b/tests/test_relative_imports.py @@ -62,7 +62,7 @@ def _dangling(path): class RelativeImportsTest(unittest.TestCase): def test_every_relative_import_resolves(self): found = [] - for top in ("lib", "apps"): + for top in ("lib", "apps", "programs"): for path, dirs, files in os.walk(os.path.join(ROOT, top)): dirs[:] = [d for d in dirs if d not in _LEFT_OUT] for name in files: diff --git a/tests/test_vamp.py b/tests/test_vamp.py index b65ca1c..7e5dd93 100644 --- a/tests/test_vamp.py +++ b/tests/test_vamp.py @@ -25,7 +25,7 @@ TESTS = Path(__file__).resolve().parent ROOT = TESTS.parent LIB = str(ROOT / "lib") -APPS = str(ROOT / "apps") # apps/vamp is the package itself +APPS = str(ROOT / "programs") # programs/vamp is the package itself sys.path.insert(0, str(TESTS)) from test_engine import _find, audio_libs # noqa: E402 @@ -39,7 +39,7 @@ def _hmilink(): candidates = [Path(explicit)] if explicit else [] for parent in TESTS.parents: candidates.append(parent / "pydevices" / "hmilink") - candidates.append(ROOT / "apps" / "ostinato" / "vendor" / "lib") + candidates.append(ROOT / "programs" / "ostinato" / "vendor" / "lib") for c in candidates: if (c / "hmilink" / "__init__.py").is_file(): return str(c) diff --git a/tests/vamp_check.py b/tests/vamp_check.py index 6b7bdf2..1fa5595 100644 --- a/tests/vamp_check.py +++ b/tests/vamp_check.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MIT -"""Checks Vamp's engine (apps/vamp/engine.py) offline, on MicroPython and CPython. +"""Checks Vamp's engine (programs/vamp/engine.py) offline, on MicroPython and CPython. Pulls the engine's output itself, so it owns the clock, and listens to what comes out: notes start on their frame however rarely the sequencer runs, an @@ -12,7 +12,7 @@ micropython -X heapsize=96M tests/vamp_check.py BANK.sf2 [--plant FAULT] MICROPYPATH (or PYTHONPATH) needs the audio libraries (audioinstruments, -audioeffects) and studio's lib/. Any General MIDI bank with a closed and an +audioeffects) and the apps repo's lib/. Any General MIDI bank with a closed and an open hi-hat in one exclusive class will do: tests/vamp_bank.py writes a small one. ``--plant`` breaks the engine one way (nodelay, nocancel, nomute, norack, nolive, nochoke) and the check must then fail. diff --git a/tests/vamp_link_contract.py b/tests/vamp_link_contract.py index 419b838..dbe0609 100644 --- a/tests/vamp_link_contract.py +++ b/tests/vamp_link_contract.py @@ -8,7 +8,7 @@ micropython -X heapsize=64M tests/vamp_link_contract.py BANK.sf2 [--plant nomute] python tests/vamp_link_contract.py BANK.sf2 -MICROPYPATH (or PYTHONPATH) needs studio's lib/, hmilink and the audio +MICROPYPATH (or PYTHONPATH) needs the apps repo's programs/ and lib/, hmilink and the audio libraries. A panel loads a song, presses Play, changes the tempo, mutes a track, adds a note, puts a reverb on the master and stops. After each step the box's song must equal the panel's, and the engine must have acted on it. diff --git a/tools/vamp_files.py b/tools/vamp_files.py index ce15744..1760dcf 100644 --- a/tools/vamp_files.py +++ b/tools/vamp_files.py @@ -22,11 +22,11 @@ import sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -OSTINATO = os.path.join(ROOT, "apps", "ostinato") +OSTINATO = os.path.join(ROOT, "programs", "ostinato") # Each package, its files, and the folder they come from in this checkout. PACKAGES = { - "vamp": (["__init__.py", "engine.py", "device.py", "main.py"], "apps"), + "vamp": (["__init__.py", "engine.py", "device.py", "main.py"], "programs"), "songmodel": (["__init__.py", "model.py", "link.py", "page.html"], "lib"), } HMILINK = ["__init__.py", "tcp.py"] diff --git a/tools/vamp_link_check.py b/tools/vamp_link_check.py index d7ecca2..c50b43f 100644 --- a/tools/vamp_link_check.py +++ b/tools/vamp_link_check.py @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: MIT -"""Drives a running Vamp (apps/vamp/main.py) over hmilink, as Ostinato's window +"""Drives a running Vamp (programs/vamp/main.py) over hmilink, as Ostinato's window does, and checks the box keeps the song and the time. python tools/vamp_link_check.py HOST:PORT [--song FILE] [--browser] [--json OUT] @@ -32,7 +32,7 @@ import time ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -OSTINATO = os.path.join(ROOT, "apps", "ostinato") +OSTINATO = os.path.join(ROOT, "programs", "ostinato") def _hmilink(): @@ -48,7 +48,7 @@ def _hmilink(): raise SystemExit("no hmilink: set HMILINK_PATH to the folder holding the hmilink package") -sys.path[0:0] = [os.path.join(ROOT, "lib"), os.path.join(ROOT, "apps"), _hmilink()] +sys.path[0:0] = [os.path.join(ROOT, "lib"), os.path.join(ROOT, "programs"), _hmilink()] import hmilink # noqa: E402 from hmilink import tcp # noqa: E402