| 1210 | wave->SetVolume(vol * baseVol, freq, true); |
| 1211 | if (is3D) |
| 1212 | wave->SetPosition(_listenerPos + _listenerDir * 50, _listenerVel, true); |
| 1213 | // Music preview loops while focused so volume tweaks are |
| 1214 | // immediately audible against a continuous track; speech/effect |
| 1215 | // remain one-shot (their TickPreviewLoop replays them every 5 s). |
| 1216 | wave->Repeat(kind == WaveMusic ? 0 : 1); |
| 1217 | wave->Play(); |
| 1218 | ++_categoryPreviewRestartCount; // audio-invariants A-18 |
| 1219 | LOG_DEBUG(Audio, "StartCategoryPreview({}): playing '{}'", slot, static_cast<const char*>(name)); |
| 1220 | } |
| 1221 | |
| 1222 | void SoundSystemOAL::StartDevicePreview() |
| 1223 | { |
| 1224 | if (!_enablePreview) |
| 1225 | return; |
| 1226 | |
| 1227 | if (_previewDevice) |
| 1228 | return; // already playing — nothing to restart |
| 1229 | SoundPars pars{}; |
| 1230 | if (!ResolvePreviewMusicTrack("deviceMusicTrack", pars) && !ResolvePreviewMusicTrack("musicTrack", pars)) |
| 1231 | ResolvePreviewSoundArray("music", pars); |
| 1232 | |
| 1233 | RStringB name = pars.name; |
| 1234 | float vol = pars.vol, freq = pars.freq; |
| 1235 | IWave* wave = CreateWave(name, false, true); |
| 1236 | _previewDevice = wave; |
| 1237 | if (!wave) |
| 1238 | { |
| 1239 | LOG_WARN(Audio, "StartDevicePreview: no preview wave found"); |
| 1240 | return; |
| 1241 | } |
| 1242 | wave->SetKind(WaveMusic); |
| 1243 | wave->EnableAccommodation(false); |
| 1244 | wave->SetSticky(true); |
| 1245 | wave->SetVolume(vol * 0.5f, freq, true); |
| 1246 | wave->Repeat(0); // 0 = forever; loops while the row is focused |
| 1247 | |
| 1248 | // First start: anchor wall clock at "now" and remember length. |
| 1249 | // Subsequent starts (after a SwitchOutputDevice teardown) compute |
| 1250 | // the resume offset from (now - anchor) % length so playback head |
| 1251 | // advances naturally through the brief context-swap silence — |
| 1252 | // rapid clicks no longer collapse the offset back to ~0. |
| 1253 | uint32_t now = GlobalTickCount(); |
| 1254 | if (_devicePreviewAnchorMs == 0) |
| 1255 | { |
| 1256 | _devicePreviewAnchorMs = now; |
| 1257 | _devicePreviewLengthSec = wave->GetLength(); |
| 1258 | } |
| 1259 | // Pure-math helper — audio-invariants A-17. |
| 1260 | const float seekTo = audio::DevicePreviewOffsetSeconds(now, _devicePreviewAnchorMs, _devicePreviewLengthSec); |
| 1261 | // Stash the seek on the wave so DoPlay applies it once the AL |
| 1262 | // source actually exists (Play() defers to next Commit, so |
| 1263 | // alSourcef here would no-op against a 0 source). |
nothing calls this directly
no test coverage detected