ludic/changes/windows-target.md
Orkuncakilkaya 3300fb3957 feat(windows): sound, through XAudio2
audio_win.ll implements audio.ll's snd_* contract over XAudio2 from IR: a
source voice per clip, restart on play, LoopCount for loops, SetVolume,
SetFrequencyRatio and a balance pan through SetOutputMatrix, with the COM
slots taken from the SDK's xaudio2.h. Clips are RIFF WAVE read through
lp_pak_open (weakly referenced), so a packed sound loads directly. ludicc links
it with xaudio2 and ole32, and silences xinput.lib's importeddllmain warning.

Verified: ludic-dev test 135/135, selfhost-test 32/32; on the PC a harness
loads, plays, pans, loops and stops clips, and Maroon Lake windowed reports
"sound: 31 of 31 clips loaded".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 02:42:48 +03:00

36 lines
2.7 KiB
Markdown

bump: minor
type: feat
**Windows target** — `ludicc` builds and runs headless programs on Windows, and builds itself there.
- **`--target <triple>`** — a triple naming `windows` selects the Windows runtime; without
the flag the target is the host, read at run time from `OS=Windows_NT`. The IR still
carries no triple, so clang assembles it for the machine it runs on.
- **The libc surface, in IR** — on Windows the header emits `emit_win.ludic`, which defines
the POSIX names the backend calls (`fopen`, `ftell`, `rename`, `opendir`, `mmap`,
`fmemopen`, `uname`, …) over the UCRT and Win32. Three of them fixed silent breakage
rather than link errors: `rename` onto an existing file (every `Fs.write_text` after the
first), a 32-bit `ftell`, and text-mode `fopen` rewriting `\n` as `\r\n`.
- **Known folders** — `Os.save_dir` / `config_dir` are `%APPDATA%\<app>`, `cache_dir` is
`%LOCALAPPDATA%\<app>`, `temp_dir` is `%TEMP%`, all with forward slashes; a bundle's
`home` line points at `%APPDATA%\<name>`.
- **The driver** — finds `C:\Program Files\LLVM\bin\clang.exe` when clang is not on
`%PATH%`, writes `.exe` outputs, speaks cmd.exe for its directory and cleanup commands,
and reads `argv[0]`, `%PATH%` and `$LUDIC_HOME` with either separator.
- **A window** — `win32.ll` is `cocoa.ll`'s contract over user32: the held-key set and frame
key in Ludic codes, the mouse in framebuffer pixels with raw input for cursor mode 2,
cursor hide/lock/confine that lets go when the window loses the foreground, XInput pads
in SDL order with rescaled deadzones, and the software `win_present`. `win32_gl.ll` puts
the WGL context on that window with vsync and borderless full screen; a headless build
links `gl_win_nowin.ll` instead. The process is per-monitor DPI aware.
- **Sound** — `audio_win.ll` is `audio.ll`'s contract over XAudio2: one source voice per
clip, loops, volume, rate and a balance pan through the output matrix, RIFF WAVE in PCM or
float. It reads a clip through the asset pack, so a bundled game's first `Audio.load`
succeeds - AVAudioPlayer takes a filesystem path, which is why macOS cannot.
- **Not yet** — the splash and `App.set_icon` do nothing, and `Http.*` is still a macOS
platform layer; a Windows build that uses it stops with a message saying so.
- **`Gl.*` on Windows** — `gl_win.ll` makes a WGL 4.1 core context on a hidden window's DC,
and `gl_thunks_win.ll` (generated by `ludic-dev glgen` beside `gl_thunks.ll`) calls
every entry point through a table `@lgl_win_load` fills from `wglGetProcAddress`, falling
back to `opengl32.dll` for the 1.1 functions it will not return. A headless GL program
renders on the GPU there: `gl_triangle` matches the macOS frame to within one level per
channel.