# FM-1 Emulator

A Rust emulator for the M-VAVE FM-1, with browser, native desktop,
and DAW plugin interfaces.

## Run locally

Requirements: Rust stable managed by rustup, Python 3, and Node.js 22 or newer
for the JavaScript tests. No npm dependencies are needed.

Install the browser compilation target once:

```sh
rustup target add wasm32-unknown-unknown --toolchain stable
```

From this directory:

```sh
./dev.sh
```

Open http://localhost:8642.
Choose a firmware and press **Start** to enable audio. Play with your computer
keyboard, the on-screen controls, or a MIDI keyboard. Firmware downloads need an
internet connection on first use; you can also open your own `.fwsc` file.
Press **?** for the keyboard shortcuts. Press **⌘K** (Ctrl+K elsewhere) or **Search** for everything besides playing: firmwares,
the clock, saves and backups, MIDI in, and the guide. Click the firmware's name to switch firmware.
Expand **Panel controls** for keyboard and screen-reader access to every control.
Use Tab to move, Space or Enter to hold buttons, and arrow keys to adjust sliders.
Settings and patches are saved in this browser. Use **Export** to back them up.
Press **Record** (or Shift+R) to record what you play; takes are kept in this browser as WAV files
you can play back, download or delete under the synth.
After the first visit the page works offline (a service worker, `web/sw.js`), including any firmware
you have played before.

Stop with Ctrl+C. Re-run the command after source changes; it rebuilds the app.
Use `PORT=9000 ./dev.sh` to choose another port. Keep the same port for normal
use because browser saves belong to that origin. The server binds to loopback.

The dialogs (command menu, keyboard shortcuts, panel controls) are React with
[shadcn/ui](https://ui.shadcn.com) components (`web/palette/src/components/ui`) and Tailwind. They are
bundled into `web/palette.js` and `web/ui.css`, both kept in the repository so the steps above need no
npm. After changing anything in `web/palette`, or the Tailwind classes in `web/index.html`:

```sh
(cd web/palette && npm install && npm run build)
```

Fonts: Inter and Berkeley Mono are in `web/fonts`.

Build without starting a server:

```sh
./web/build.sh
```

The output is `web/dist/`. Serve that directory over HTTP; opening the HTML as a
file will not work. `RUSTUP_TOOLCHAIN` selects another installed Rust toolchain.

## Hosting on Cloudflare

The site is static: `wrangler.jsonc` puts `web/dist/` on Cloudflare Workers as static assets, with
the response headers in `web/_headers`. Run `npx wrangler login` once, then:

```sh
SITE_URL=https://fm1.dhrv.pw tools/deploy_cloudflare.sh
```

`SITE_URL` is the address the site is served from. The build writes it into the canonical links,
the share image tags, `sitemap.xml` and `robots.txt` (`tools/site_meta.py`). Add the domain to the
Worker under Settings → Domains & Routes. The share image is `web/og.png`, drawn from
`tools/og.html`.

To deploy on every push, connect the repository to the Worker in Cloudflare (Settings → Build) with
the build command `./tools/cloudflare_build.sh` and the deploy command `npx wrangler deploy`.

## Checks

```sh
node --test web/*.test.mjs
rustup run stable cargo test --locked --release -p fm1-isa -p fm1-core -p fm1-soc -p fm1-web
./web/build.sh
node web/smoke.mjs
node web/persistence.wasm.mjs
```

The smoke and WASM persistence checks download real firmware packages.

## Native app and plugin

Run a local firmware package in the desktop app:

```sh
rustup run stable cargo run --locked --release -p fm1-live -- --fwsc /path/to/firmware.fwsc
```

Linux native builds also require ALSA development headers and pkg-config.
See [plugin setup](crates/fm1-plugin/README.md) for AU, VST3, and CLAP builds.

## Project layout

- `crates/`: CPU, instruction decoder, peripherals, native app, plugin, and WASM interface.
- `web/`: browser interface, firmware catalogs, audio worklet, and tests.
- `isa/`, `probes/`, `tools/`: instruction-set research, hardware probes, and developer utilities.
- `work/`: ISA solver data used by the developer tools; keep these inputs.
- `docs/`: [hardware notes](docs/recon.md), [ISA notes](docs/isa-notes.md), and
  [stock firmware notes](docs/stock-firmware.md).

The emulator is distributed under the [MIT license](LICENSE). Firmware packages
are separate works with their own licenses, recorded in the firmware catalogs.
