| 1436 | // transient underrun / deferred start also reads AL_STOPPED — |
| 1437 | // UpdateStreamingPlayback re-primes and restarts it, so it is |
| 1438 | // NOT stopped. Only a genuine end-of-stream terminates here. |
| 1439 | if (alState == AL_STOPPED && _playing && !IsStreamingUnderrun()) |
| 1440 | { |
| 1441 | _playing = false; |
| 1442 | _state.playing = false; |
| 1443 | _state.terminated = true; |
| 1444 | _streamPlayIntent = false; |
| 1445 | OnTerminateOnce(); |
| 1446 | } |
| 1447 | } |
| 1448 | |
| 1449 | return !_playing; |
| 1450 | } |
| 1451 | |
| 1452 | bool WaveOAL::IsTerminated() |
| 1453 | { |
| 1454 | // Deferred play: not terminated if about to play |
| 1455 | if (_wantPlaying) |
| 1456 | return false; |
| 1457 | |
| 1458 | // Detect natural completion → set _state.terminated. A streamed |
| 1459 | // source that stopped from a starvation underrun or a not-yet-started |
| 1460 | // deferred play also reads AL_STOPPED; UpdateStreamingPlayback restarts |
| 1461 | // it, so it must NOT be reaped here. Only a genuine end-of-stream |
| 1462 | // terminates. |
| 1463 | if (!_state.terminated && _alSource && _playing) |
| 1464 | { |
| 1465 | ALint alState = AL_STOPPED; |
| 1466 | alGetSourcei(_alSource, AL_SOURCE_STATE, &alState); |
| 1467 | if (alState == AL_STOPPED && !IsStreamingUnderrun()) |
| 1468 | { |
| 1469 | _playing = false; |
| 1470 | _state.playing = false; |
| 1471 | _state.terminated = true; |
| 1472 | _streamPlayIntent = false; |
| 1473 | OnTerminateOnce(); |
| 1474 | } |
| 1475 | } |
| 1476 | |
| 1477 | if (_loadError) |
| 1478 | _state.terminated = true; |
| 1479 | |
| 1480 | if (!_state.terminated) |
| 1481 | return false; |
| 1482 | |
| 1483 | // I am terminated. Activate the next wave in the queue chain (1.99 |
| 1484 | // WaveGlobal8::AdvanceQueue — soundDX8.cpp:420). Idempotent: the |
| 1485 | // successor's _queued flag is the source of truth — once cleared, |
| 1486 | // we don't re-activate. Restart()ing the head also re-arms this |
| 1487 | // path for the next cycle since Restart() may flip the successor |
| 1488 | // back into _queued state externally. |
| 1489 | if (_queueNext && _queueNext->_queued) |
| 1490 | { |
| 1491 | _queueNext->_queued = false; |
| 1492 | _queueNext->Play(); |
no test coverage detected