0.9.11-rc-2
Switch from JACK to ALSA audio.
This commit is contained in:
@@ -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
@@ -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[] = [];
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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 </Typography>
|
||||
<Typography variant="caption" color="inherit">{status.errorMessage === "" ? "Stopped" : status.errorMessage} </Typography>
|
||||
</span>
|
||||
{
|
||||
status.temperaturemC > -100000 &&
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user