From 419d60d7644c949039327f5cfa8f39dc7604c0af Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Tue, 3 Sep 2024 20:57:02 -0400 Subject: [PATCH] Correct configuration of buffers for devices with minBufferSize=256 --- react/src/JackServerSettingsDialog.tsx | 243 ++++++++++++++++++++----- 1 file changed, 200 insertions(+), 43 deletions(-) diff --git a/react/src/JackServerSettingsDialog.tsx b/react/src/JackServerSettingsDialog.tsx index 5277823..d3d2e3a 100644 --- a/react/src/JackServerSettingsDialog.tsx +++ b/react/src/JackServerSettingsDialog.tsx @@ -42,11 +42,21 @@ import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel'; import AlsaDeviceInfo from './AlsaDeviceInfo'; +const MIN_BUFFER_SIZE = 16; +const MAX_BUFFER_SIZE = 2048; + + +interface BufferSetting { + bufferSize: number; + numberOfBuffers: number; +}; + const INVALID_DEVICE_ID = "_invalid_"; interface JackServerSettingsDialogState { latencyText: string; jackServerSettings: JackServerSettings; alsaDevices?: AlsaDeviceInfo[]; + okEnabled: boolean; } const styles = (theme: Theme) => @@ -66,12 +76,150 @@ export interface JackServerSettingsDialogProps extends WithStyles onApply: (jackServerSettings: JackServerSettings) => void; } -function getLatencyText(settings: JackServerSettings ): string { +function getLatencyText(settings?: JackServerSettings ): string { + if (!settings) + { + return "\u00A0"; + } + if (!settings.valid) return "\u00A0"; let ms = settings.bufferSize * settings.numberOfBuffers / settings.sampleRate * 1000; return ms.toFixed(1) + "ms"; } +function getValidBufferCounts(bufferSize: number, alsaDeviceInfo?: AlsaDeviceInfo): number[] +{ + if (!alsaDeviceInfo) + { + return []; + } + let result: number[] = []; + if (bufferSize * 2 >= alsaDeviceInfo.minBufferSize) + { + result = [2,3,4]; + } else { + let minBuffers = Math.ceil(alsaDeviceInfo.minBufferSize/bufferSize/2-0.0001); + result = [minBuffers*2,minBuffers*3, minBuffers*4]; + } + for (let i = 0; i < result.length; ++i) + { + let selectedSize = result[i]*bufferSize; + if (selectedSize < alsaDeviceInfo.minBufferSize || selectedSize > alsaDeviceInfo.maxBufferSize) + { + result.splice(i,1); + --i; + } + } + + return result; +} +function getValidBufferSizes(alsaDeviceInfo? : AlsaDeviceInfo): number[] +{ + if (!alsaDeviceInfo ) + { + return []; + } + let result: number[] = []; + for (let i = MIN_BUFFER_SIZE; i <= MAX_BUFFER_SIZE; i *= 2) + { + if (getValidBufferCounts(i,alsaDeviceInfo).length !== 0) + { + result.push(i); + } + } + return result; +} +function getBestBuffers( + alsaDeviceInfo: AlsaDeviceInfo | undefined, + bufferSize: number, + numberOfBuffers: number, + bufferSizeChanging: boolean = false +) : BufferSetting { + if (!alsaDeviceInfo) + { + return { bufferSize:bufferSize, numberOfBuffers: numberOfBuffers}; + } + + // If the numberOfbuffers is fine as is, don't change the number of Buffers. + // set default values. Otherwise, choose the best buffer count from available buffer counts. + let validBuffercounts = getValidBufferCounts(bufferSize,alsaDeviceInfo); + let ix = validBuffercounts.indexOf(numberOfBuffers); + if (ix !== -1) { + if (bufferSize*numberOfBuffers <= alsaDeviceInfo.maxBufferSize + && bufferSize*numberOfBuffers >= alsaDeviceInfo.minBufferSize) + { + return {bufferSize: bufferSize, numberOfBuffers: numberOfBuffers}; + } + } + // if numberOfBuffers is not valid, and the user has just selected a new buffers size, + // then choose from available buffer counts. + if (bufferSizeChanging) + { + if (validBuffercounts.length !== 0) + { + // prefer the one thats divisible by 3. + for (let i = 0; i < validBuffercounts.length; ++i) + { + if (validBuffercounts[i] % 3 === 0) + { + numberOfBuffers = validBuffercounts[i]; + return {bufferSize: bufferSize, numberOfBuffers:numberOfBuffers}; + } + } + numberOfBuffers = validBuffercounts[0]; + return {bufferSize: bufferSize, numberOfBuffers:numberOfBuffers}; + } + } + // otherwise select a sensible starting value. + + // favored default: 64x3. + if (64*3 >= alsaDeviceInfo.minBufferSize && 64*3 <= alsaDeviceInfo.maxBufferSize) + { + return { bufferSize: 64, numberOfBuffers: 3}; + } + // if that isn't possible then minBufferSize/2 x 4. + bufferSize = alsaDeviceInfo.minBufferSize/2; + numberOfBuffers = 4; + // otherwise, minBufferSize/2 x 2. + if (bufferSize*numberOfBuffers > alsaDeviceInfo.maxBufferSize) + { + numberOfBuffers = 2; + } + return { bufferSize: bufferSize, numberOfBuffers: numberOfBuffers}; + +}; + +function isOkEnabled(jackServerSettings: JackServerSettings, alsaDevices?: AlsaDeviceInfo[]) +{ + if (!jackServerSettings.valid) return false; + if (!alsaDevices) return false; + let alsaDevice: AlsaDeviceInfo | undefined = undefined; + for (let i = 0; i < alsaDevices.length; ++i) + { + if (alsaDevices[i].id === jackServerSettings.alsaDevice) + { + alsaDevice = alsaDevices[i]; + } + } + if (!alsaDevice) + { + return false; + } + let deviceBufferSize = jackServerSettings.bufferSize * jackServerSettings.numberOfBuffers; + if (deviceBufferSize < alsaDevice.minBufferSize || deviceBufferSize > alsaDevice.maxBufferSize) + { + return false; + } + let validBufferCounts = getValidBufferCounts(jackServerSettings.bufferSize, alsaDevice); + let ix = validBufferCounts.indexOf(jackServerSettings.numberOfBuffers); + if (ix === -1) + { + return false; + } + return true; +} + + const JackServerSettingsDialog = withStyles(styles)( class extends Component { @@ -83,8 +231,10 @@ const JackServerSettingsDialog = withStyles(styles)( this.state = { - latencyText: "\u00A0", - jackServerSettings: props.jackServerSettings.clone() // invalid, but not nullish + latencyText: getLatencyText(props.jackServerSettings), + jackServerSettings: props.jackServerSettings.clone(), // invalid, but not nullish + alsaDevices: undefined, + okEnabled: false }; } mounted: boolean = false; @@ -98,7 +248,8 @@ const JackServerSettingsDialog = withStyles(styles)( this.setState({ alsaDevices: devices, jackServerSettings: settings, - latencyText: getLatencyText(settings) + latencyText: getLatencyText(settings), + okEnabled: isOkEnabled(settings,devices) }); } else { this.setState({ alsaDevices: devices }); @@ -115,8 +266,8 @@ const JackServerSettingsDialog = withStyles(styles)( if (!alsaDevices) { return result; } + result.valid = false; if (alsaDevices.length === 0) { - result.valid = false; result.alsaDevice = INVALID_DEVICE_ID; return result; } @@ -134,27 +285,34 @@ const JackServerSettingsDialog = withStyles(styles)( } if (result.sampleRate === 0) result.sampleRate = 48000; - if (result.bufferSize === 0) result.bufferSize = 16; - if (result.numberOfBuffers === 0) result.numberOfBuffers = 3; result.sampleRate = selectedDevice.closestSampleRate(result.sampleRate); - result.bufferSize = selectedDevice.closestBufferSize(result.bufferSize); + + let bestBuffers = getBestBuffers(selectedDevice,result.bufferSize,result.numberOfBuffers); + result.bufferSize = bestBuffers.bufferSize; + result.numberOfBuffers = bestBuffers.numberOfBuffers; + result.valid = true; return result; } componentDidMount() { this.mounted = true; - this.requestAlsaInfo(); + if (this.props.open) { + this.requestAlsaInfo(); + } } componentDidUpdate(oldProps: JackServerSettingsDialogProps) { - if (this.props.open && !oldProps.open) { + if ((this.props.open && !oldProps.open) && this.mounted) { let settings = this.applyAlsaDevices(this.props.jackServerSettings.clone(), this.state.alsaDevices); this.setState({ jackServerSettings: settings, - latencyText: getLatencyText(settings) + latencyText: getLatencyText(settings), + okEnabled: isOkEnabled(settings,this.state.alsaDevices) }); - this.requestAlsaInfo(); + if (!this.state.alsaDevices) { + this.requestAlsaInfo(); + } } } @@ -180,12 +338,12 @@ const JackServerSettingsDialog = withStyles(styles)( if (!selectedDevice) return; let settings = this.state.jackServerSettings.clone(); settings.alsaDevice = device; - settings.sampleRate = selectedDevice.closestSampleRate(settings.sampleRate); - settings.bufferSize = selectedDevice.closestBufferSize(settings.bufferSize); + settings = this.applyAlsaDevices(settings,this.state.alsaDevices); this.setState({ jackServerSettings: settings, - latencyText: getLatencyText(settings) + latencyText: getLatencyText(settings), + okEnabled: isOkEnabled(settings,this.state.alsaDevices) }); } handleRateChanged(e: any) { @@ -198,7 +356,8 @@ const JackServerSettingsDialog = withStyles(styles)( this.setState({ jackServerSettings: settings, - latencyText: getLatencyText(settings) + latencyText: getLatencyText(settings), + okEnabled: isOkEnabled(settings,this.state.alsaDevices) }); } handleSizeChanged(e: any) { @@ -208,10 +367,14 @@ const JackServerSettingsDialog = withStyles(styles)( if (!selectedDevice) return; let settings = this.state.jackServerSettings.clone(); settings.bufferSize = size; + let bestBufferSetting = getBestBuffers(selectedDevice,settings.bufferSize,settings.numberOfBuffers,true); + settings.bufferSize = bestBufferSetting.bufferSize; + settings.numberOfBuffers = bestBufferSetting.numberOfBuffers; this.setState({ jackServerSettings: settings, - latencyText: getLatencyText(settings) + latencyText: getLatencyText(settings), + okEnabled: isOkEnabled(settings,this.state.alsaDevices) }); } handleNumberOfBuffersChanged(e: any) { @@ -224,7 +387,8 @@ const JackServerSettingsDialog = withStyles(styles)( this.setState({ jackServerSettings: settings, - latencyText: getLatencyText(settings) + latencyText: getLatencyText(settings), + okEnabled: isOkEnabled(settings,this.state.alsaDevices) }); } @@ -232,19 +396,6 @@ const JackServerSettingsDialog = withStyles(styles)( this.props.onApply(this.state.jackServerSettings.clone()); }; - getBufferSizes(alsaDevice: AlsaDeviceInfo): number[] { - let result: number[] = []; - let max = alsaDevice.maxBufferSize; - if (max > 2048) max = 2048; - - for (let i = 16; i <= max; i *= 2) { - //if (i >= alsaDevice.minBufferSize) { // yyy should be MxN >= minBufferSize, or perhaps MxNx2 >= minBufferSize - result.push(i); - //} - } - return result; - } - render() { const classes = this.props.classes; @@ -265,10 +416,10 @@ const JackServerSettingsDialog = withStyles(styles)( } } } - let bufferSizes: number[] = []; - if (selectedDevice) { - bufferSizes = this.getBufferSizes(selectedDevice); - } + let bufferSizes: number[] = getValidBufferSizes(selectedDevice); + let bufferCounts = getValidBufferCounts(this.state.jackServerSettings.bufferSize,selectedDevice); + let bufferSizeDisabled = !selectedDevice; + let bufferCountDisabled = !selectedDevice; return ( @@ -305,6 +456,7 @@ const JackServerSettingsDialog = withStyles(styles)( this.handleSizeChanged(e)} value={this.state.jackServerSettings.bufferSize} + disabled={bufferSizeDisabled} inputProps={{ name: 'Buffer size', id: 'jsd_bufferSize', @@ -343,17 +496,21 @@ const JackServerSettingsDialog = withStyles(styles)( @@ -368,8 +525,8 @@ const JackServerSettingsDialog = withStyles(styles)( -