0.9.11-rc-2

Switch from JACK to ALSA audio.
This commit is contained in:
Robin Davies
2022-07-01 21:15:57 -04:00
parent 0995e0fb16
commit 0a67b39103
51 changed files with 4301 additions and 632 deletions
+45
View File
@@ -0,0 +1,45 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
export class AlsaMidiDeviceInfo {
deserialize(input: any) : AlsaMidiDeviceInfo{
this.name = input.name;
this.description = input.description;
return this;
}
static deserializeArray(input: any[]): AlsaMidiDeviceInfo[] {
let result: AlsaMidiDeviceInfo[] = [];
for (let i = 0; i < input.length; ++i) {
result[i] = new AlsaMidiDeviceInfo().deserialize(input[i]);
}
return result;
}
name: string = "";
description: string = "";
equals(other: AlsaMidiDeviceInfo) : boolean {
return this.name === other.name && this.description === other.description;
}
}
+7 -6
View File
@@ -18,13 +18,14 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import {PiPedalArgumentError} from './PiPedalError';
import {AlsaMidiDeviceInfo} from './AlsaMidiDeviceInfo';
export class JackChannelSelection {
deserialize(input: any): JackChannelSelection
{
this.inputAudioPorts = input.inputAudioPorts.slice();
this.outputAudioPorts = input.outputAudioPorts.slice();
this.inputMidiPorts = input.inputMidiPorts.slice();
this.inputMidiDevices = AlsaMidiDeviceInfo.deserializeArray(input.inputMidiDevices);
this.sampleRate = input.sampleRate;
this.bufferSize = input.bufferSize;
this.numberOfBuffers = input.numberOfBuffers;
@@ -35,7 +36,7 @@ export class JackChannelSelection {
}
inputAudioPorts: string[] = [];
outputAudioPorts: string[] = [];
inputMidiPorts: string[] = [];
inputMidiDevices: AlsaMidiDeviceInfo[] = [];
sampleRate: number = 48000;
bufferSize: number = 64;
@@ -45,6 +46,7 @@ export class JackChannelSelection {
let result = new JackChannelSelection();
result.inputAudioPorts = jackConfiguration.inputAudioPorts.slice(0,2);
result.outputAudioPorts = jackConfiguration.inputAudioPorts.slice(0,2);
result.inputMidiDevices = [];
return result;
}
getChannelDisplayValue(selectedChannels: string[], availableChannels: string[],isConfigValid: boolean): string
@@ -97,8 +99,7 @@ export class JackConfiguration {
this.maxAllowedMidiDelta = input.maxAllowedMidiDelta;
this.inputAudioPorts = input.inputAudioPorts;
this.outputAudioPorts = input.outputAudioPorts;
this.inputMidiPorts = input.inputMidiPorts;
this.outputMidiPorts = input.outputMidiPorts;
this.inputMidiDevices = AlsaMidiDeviceInfo.deserializeArray(input.inputMidiDevices);
return this;
}
isValid: boolean = false;
@@ -111,8 +112,8 @@ export class JackConfiguration {
inputAudioPorts: string[] = [];
outputAudioPorts: string[] = [];
inputMidiPorts: string[] = [];
outputMidiPorts: string[] = [];
inputMidiDevices: AlsaMidiDeviceInfo[] = [];
};
+3 -1
View File
@@ -58,6 +58,7 @@ export default class JackHostStatus {
deserialize(input: any): JackHostStatus {
this.active = input.active;
this.restarting = input.restarting;
this.errorMessage = input.errorMessage;
this.underruns = input.underruns;
this.cpuUsage = input.cpuUsage;
this.msSinceLastUnderrun = input.msSinceLastUnderrun;
@@ -71,6 +72,7 @@ export default class JackHostStatus {
return this.temperaturemC >= -100000;
}
active: boolean = false;
errorMessage: string = "";
restarting: boolean = false;
underruns: number = 0;
cpuUsage: number = 0;
@@ -136,7 +138,7 @@ export default class JackHostStatus {
<Typography variant="caption" color="textSecondary">{label}</Typography>
<span style={{ color: RED_COLOR }}>
<Typography variant="caption" color="inherit">Stopped&nbsp;&nbsp;</Typography>
<Typography variant="caption" color="inherit">{status.errorMessage === "" ? "Stopped" : status.errorMessage}&nbsp;&nbsp;</Typography>
</span>
{
status.temperaturemC > -100000 &&
+1 -1
View File
@@ -135,7 +135,7 @@ const JackServerSettingsDialog = withStyles(styles)(
}
if (result.sampleRate === 0) result.sampleRate = 48000;
if (result.bufferSize === 0) result.bufferSize = 64;
if (result.numberOfBuffers === 0) result.numberOfBuffers = 3;
if (result.numberOfBuffers === 0) result.numberOfBuffers = 2;
result.sampleRate = selectedDevice.closestSampleRate(result.sampleRate);
result.bufferSize = selectedDevice.closestBufferSize(result.bufferSize);
result.valid = true;
+1 -1
View File
@@ -96,6 +96,7 @@ export const MidiBindingDialog =
hasHooks: boolean = false;
handleClose() {
this.cancelListenForControl();
this.props.onClose();
}
@@ -120,7 +121,6 @@ export const MidiBindingDialog =
handleListenSucceeded(instanceId: number, symbol: string, isNote: boolean, noteOrControl: number)
{
this.listenHandle = undefined; // (one-shot event)
this.cancelListenForControl();
let pedalBoard = this.model.pedalBoard.get();
+6
View File
@@ -1826,12 +1826,18 @@ class PiPedalModelImpl implements PiPedalModel {
}
}
cancelListenForMidiEvent(listenHandle: ListenHandle): void {
let found = false;
for (let i = 0; i < this.midiListeners.length; ++i) {
if (this.midiListeners[i].handle === listenHandle._handle) {
this.midiListeners.splice(i, 1);
found = true;
break;
}
}
if (!found)
{
console.log('cancelListenForMidiEvent: event not found.');
}
this.webSocket?.send("cancelListenForMidiEvent", listenHandle._handle);
}
cancelListenForAtomOutput(listenHandle: ListenHandle): void {
+20 -15
View File
@@ -27,38 +27,43 @@ import Dialog from '@mui/material/Dialog';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import {AlsaMidiDeviceInfo} from './AlsaMidiDeviceInfo';
export interface SelectMidiChannelsDialogProps {
open: boolean;
selectedChannels: string[];
availableChannels: string[];
onClose: (selectedChannels: string[] | null) => void;
selectedChannels: AlsaMidiDeviceInfo[];
availableChannels: AlsaMidiDeviceInfo[];
onClose: (selectedChannels: AlsaMidiDeviceInfo[] | null) => void;
}
function isChecked(selectedChannels: string[], channel: string): boolean {
function isChecked(selectedChannels: AlsaMidiDeviceInfo[], channel: AlsaMidiDeviceInfo): boolean {
for (let i = 0; i < selectedChannels.length; ++i) {
if (selectedChannels[i] === channel) return true;
if (selectedChannels[i].equals(channel)) return true;
}
return false;
}
function addPort(availableChannels: string[], selectedChannels: string[], newChannel: string) {
let result: string[] = [];
function addPort(availableChannels: AlsaMidiDeviceInfo[], selectedChannels: AlsaMidiDeviceInfo[], newChannel: AlsaMidiDeviceInfo)
:AlsaMidiDeviceInfo[]
{
let result: AlsaMidiDeviceInfo[] = [];
for (let i = 0; i < availableChannels.length; ++i) {
let channel = availableChannels[i];
if (isChecked(selectedChannels, channel) || channel === newChannel) {
if (isChecked(selectedChannels, channel) || channel.equals(newChannel)) {
result.push(channel);
}
}
return result;
}
function removePort(selectedChannels: string[], channel: string) {
let result: string[] = [];
function removePort(selectedChannels: AlsaMidiDeviceInfo[], channel: AlsaMidiDeviceInfo)
:AlsaMidiDeviceInfo[]
{
let result: AlsaMidiDeviceInfo[] = [];
for (let i = 0; i < selectedChannels.length; ++i) {
if (selectedChannels[i] !== channel) {
if (!selectedChannels[i].equals(channel)) {
result.push(selectedChannels[i]);
}
}
@@ -72,7 +77,7 @@ function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) {
let toggleSelect = (value: string) => {
let toggleSelect = (value: AlsaMidiDeviceInfo) => {
if (!isChecked(currentSelection, value)) {
setCurrentSelection(addPort(availableChannels, currentSelection, value));
} else {
@@ -90,15 +95,15 @@ function SelectMidiChannelsDialog(props: SelectMidiChannelsDialogProps) {
return (
<Dialog onClose={handleClose} aria-labelledby="select-channels-title" open={open}>
<DialogTitle id="simple-dialog-title">Select Channels</DialogTitle>
<DialogTitle id="simple-dialog-title">Select MIDI Device</DialogTitle>
<List>
{availableChannels.map((channel) => (
<ListItem button >
<FormControlLabel
control={
<Checkbox checked={isChecked(currentSelection, channel)} onClick={() => toggleSelect(channel)} key={channel} />
<Checkbox checked={isChecked(currentSelection, channel)} onClick={() => toggleSelect(channel)} key={channel.name} />
}
label={channel}
label={channel.description}
/>
</ListItem>
)
+10 -9
View File
@@ -42,6 +42,7 @@ import WifiConfigDialog from './WifiConfigDialog';
import WifiDirectConfigDialog from './WifiDirectConfigDialog';
import DialogEx from './DialogEx'
import GovernorSettings from './GovernorSettings';
import {AlsaMidiDeviceInfo} from './AlsaMidiDeviceInfo';
import Slide, { SlideProps } from '@mui/material/Slide';
import { createStyles, Theme } from '@mui/material/styles';
@@ -390,10 +391,10 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
});
}
handleSelectMidiDialogResult(channels: string[] | null): void {
handleSelectMidiDialogResult(channels: AlsaMidiDeviceInfo[] | null): void {
if (channels) {
let newSelection = this.state.jackSettings.clone();
newSelection.inputMidiPorts = channels;
let newSelection: JackChannelSelection = this.state.jackSettings.clone();
newSelection.inputMidiDevices = channels;
this.model.setJackSettings(newSelection);
}
@@ -430,9 +431,9 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
}
midiSummary(): string {
let ports = this.state.jackSettings.inputMidiPorts;
let ports = this.state.jackSettings.inputMidiDevices;
if (ports.length === 0) return "Disabled";
if (ports.length === 1) return ports[0];
if (ports.length === 1) return ports[0].description;
return ports.length + " channels";
}
handleShowWifiConfigDialog() {
@@ -592,7 +593,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<ButtonBase className={classes.setting} disabled={!isConfigValid} onClick={() => this.handleMidiSelection()} >
<SelectHoverBackground selected={false} showHover={true} />
<div style={{ width: "100%" }}>
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>Select MIDI input channels</Typography>
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>Select MIDI input</Typography>
<Typography className={classes.secondaryItem} display="block" variant="caption" color="textSecondary" noWrap>{this.midiSummary()}</Typography>
</div>
@@ -767,9 +768,9 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
(this.state.showMidiSelectDialog) &&
(
<SelectMidiChannelsDialog open={this.state.showMidiSelectDialog}
onClose={(selectedChannels: string[] | null) => this.handleSelectMidiDialogResult(selectedChannels)}
selectedChannels={this.state.jackSettings.inputMidiPorts}
availableChannels={this.state.jackConfiguration.inputMidiPorts}
onClose={(selectedChannels: AlsaMidiDeviceInfo[] | null) => this.handleSelectMidiDialogResult(selectedChannels)}
selectedChannels={this.state.jackSettings.inputMidiDevices}
availableChannels={this.state.jackConfiguration.inputMidiDevices}
/>
)