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

2.7 KiB

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.