mirror of
https://github.com/got-feedBack/feedBack-desktop.git
synced 2026-07-20 03:41:33 +00:00
Post-open verification previously failed closed on any buffer-size delta on every backend. The wedge this guards against (a driver accepting a request without changing its buffer, then blocking the next in-place request) is ASIO behaviour; ALSA rounds requests to period constraints and CoreAudio can clamp, and both previously worked by storing the driver-adjusted actuals. Keep exact buffer equality for ASIO only; other backends log and accept the adjusted size. Rate and channel-mask verification stay strict everywhere. Also from review: print the real `options.compatible` in the live-probe log instead of a hard-coded 1, and document that the live-endpoint reuse in probeDual is safe only under runDeviceLifecycleOp's message-thread serialisation (PR #113). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
93 lines
4.7 KiB
JavaScript
93 lines
4.7 KiB
JavaScript
'use strict';
|
|
|
|
// Source-level lifecycle contract for the hardware-dependent half of the ASIO
|
|
// repair. The pure format decision table is covered by rate_match_test.cpp;
|
|
// these assertions pin the ordering that cannot be exercised without loading
|
|
// a real Windows ASIO driver in CI.
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
|
|
const source = fs.readFileSync(
|
|
path.join(__dirname, '..', 'src', 'audio', 'engine', 'DeviceSetup.cpp'),
|
|
'utf8').replace(/\r\n/g, '\n');
|
|
|
|
const start = source.indexOf('juce::String DeviceSetup::applyDuplex');
|
|
const end = source.indexOf('DeviceConfigResult DeviceSetup::applySplit', start);
|
|
// Fail with a clear message before any slicing if the function markers move —
|
|
// a bad slice would otherwise make every assertion below fail confusingly.
|
|
assert.ok(start >= 0 && end > start, 'could not locate applyDuplex in DeviceSetup.cpp');
|
|
const applyDuplex = source.slice(start, end);
|
|
const probeStart = source.indexOf('DeviceOptions DeviceSetup::probeDual');
|
|
const probeEnd = source.indexOf('juce::String DeviceSetup::applyDuplex', probeStart);
|
|
const probeDual = source.slice(probeStart, probeEnd);
|
|
|
|
test('duplex probe reuses an exact live endpoint before constructing a competing device', () => {
|
|
assert.ok(probeStart >= 0 && probeEnd > probeStart, 'could not locate probeDual');
|
|
|
|
const inspectLive = probeDual.indexOf('inMgr.getCurrentAudioDevice()');
|
|
const exactEndpoint = probeDual.indexOf('const bool requestedEndpointIsLive');
|
|
const requireOpen = probeDual.indexOf('liveDevice->isOpen()', exactEndpoint);
|
|
const reuseChannels = probeDual.indexOf(
|
|
'options.inputChannels = liveDevice->getInputChannelNames()');
|
|
const temporaryProbe = probeDual.indexOf(
|
|
'inputType->createDevice(probeOutputName, probeInputName)');
|
|
|
|
assert.ok(inspectLive >= 0 && exactEndpoint > inspectLive,
|
|
'the probe must inspect and identity-check the live endpoint');
|
|
assert.ok(requireOpen > exactEndpoint && reuseChannels > requireOpen
|
|
&& temporaryProbe > reuseChannels,
|
|
'matching live capabilities must be returned before a temporary device is created');
|
|
});
|
|
|
|
test('duplex setup closes Windows ASIO before constructing its channel probe', () => {
|
|
assert.match(
|
|
applyDuplex,
|
|
/#elif JUCE_WINDOWS\s+closeBeforeReconfigure = \(currentTypeName == "ASIO"\);/);
|
|
|
|
const guardedClose = applyDuplex.indexOf(
|
|
'if (closeBeforeReconfigure && inMgr.getCurrentAudioDevice() != nullptr)');
|
|
const close = applyDuplex.indexOf('inMgr.closeAudioDevice();', guardedClose);
|
|
const probe = applyDuplex.indexOf('type->createDevice(outputName, inputName)');
|
|
assert.ok(guardedClose >= 0 && close > guardedClose && probe > close,
|
|
'the live ASIO device must be closed before a temporary probe is created');
|
|
});
|
|
|
|
test('duplex setup never converts requested-device failure into default-device success', () => {
|
|
assert.doesNotMatch(applyDuplex, /initialiseWithDefaultDevices/);
|
|
assert.match(applyDuplex, /return failClosed\("device setup failed: " \+ result\);/);
|
|
});
|
|
|
|
test('duplex success is gated on actual rate, buffer, and active channel masks', () => {
|
|
assert.match(applyDuplex, /getCurrentSampleRate\(\)/);
|
|
assert.match(applyDuplex, /getCurrentBufferSizeSamples\(\)/);
|
|
assert.match(applyDuplex, /getActiveInputChannels\(\)/);
|
|
assert.match(applyDuplex, /getActiveOutputChannels\(\)/);
|
|
assert.match(applyDuplex, /validateOpenedDeviceFormat\(/);
|
|
});
|
|
|
|
test('strict buffer-size verification is gated to ASIO; other backends accept driver-adjusted', () => {
|
|
// ALSA rounds to period constraints and CoreAudio can clamp; both must
|
|
// keep their pre-#114 store-the-actuals behaviour instead of failing
|
|
// closed on a working driver-rounded open.
|
|
assert.match(applyDuplex,
|
|
/const bool strictBufferSize = \(configuredTypeName == "ASIO"\);/);
|
|
assert.match(applyDuplex,
|
|
/strictBufferSize \? setup\.bufferSize : bs/);
|
|
assert.match(applyDuplex, /accepting driver-adjusted buffer size/);
|
|
});
|
|
|
|
test('duplex failures clear observable format state and release monitor resources', () => {
|
|
const cleanupStart = applyDuplex.indexOf('auto failClosed =');
|
|
const cleanupEnd = applyDuplex.indexOf('// Channel masks must match too', cleanupStart);
|
|
const cleanup = applyDuplex.slice(cleanupStart, cleanupEnd);
|
|
|
|
assert.match(cleanup, /inMgr\.closeAudioDevice\(\)/);
|
|
assert.match(cleanup, /currentSampleRate\.store\(0\.0/);
|
|
assert.match(cleanup, /inputBlockSize\.store\(0/);
|
|
assert.match(cleanup, /outputBlockSize\.store\(0/);
|
|
assert.match(cleanup, /monitorChain\.releaseMonitorChain\(\)/);
|
|
});
|