feat: save MIDI tracks to generation area; playing tracks and audio preview are still under development
This commit is contained in:
parent
34112c79c7
commit
a2062ae9cc
@ -23,6 +23,7 @@ type MIDIMessage struct {
|
|||||||
|
|
||||||
var ports []Port
|
var ports []Port
|
||||||
var input rtmidi.MIDIIn
|
var input rtmidi.MIDIIn
|
||||||
|
var out rtmidi.MIDIOut
|
||||||
var activeIndex int = -1
|
var activeIndex int = -1
|
||||||
var lastNoteTime time.Time
|
var lastNoteTime time.Time
|
||||||
|
|
||||||
@ -33,6 +34,14 @@ func (a *App) midiLoop() {
|
|||||||
runtime.EventsEmit(a.ctx, "midiError", err.Error())
|
runtime.EventsEmit(a.ctx, "midiError", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
out, err = rtmidi.NewMIDIOutDefault()
|
||||||
|
if err != nil {
|
||||||
|
runtime.EventsEmit(a.ctx, "midiError", err.Error())
|
||||||
|
}
|
||||||
|
err = out.OpenPort(0, "")
|
||||||
|
if err != nil {
|
||||||
|
runtime.EventsEmit(a.ctx, "midiError", err.Error())
|
||||||
|
}
|
||||||
ticker := time.NewTicker(500 * time.Millisecond)
|
ticker := time.NewTicker(500 * time.Millisecond)
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -55,7 +64,7 @@ func (a *App) midiLoop() {
|
|||||||
|
|
||||||
func (a *App) OpenMidiPort(index int) error {
|
func (a *App) OpenMidiPort(index int) error {
|
||||||
if input == nil {
|
if input == nil {
|
||||||
return errors.New("failed to initialize MIDI")
|
return errors.New("failed to initialize MIDI input")
|
||||||
}
|
}
|
||||||
if activeIndex == index {
|
if activeIndex == index {
|
||||||
return nil
|
return nil
|
||||||
@ -126,7 +135,7 @@ func (a *App) OpenMidiPort(index int) error {
|
|||||||
|
|
||||||
func (a *App) CloseMidiPort() error {
|
func (a *App) CloseMidiPort() error {
|
||||||
if input == nil {
|
if input == nil {
|
||||||
return errors.New("failed to initialize MIDI")
|
return errors.New("failed to initialize MIDI input")
|
||||||
}
|
}
|
||||||
if activeIndex == -1 {
|
if activeIndex == -1 {
|
||||||
return nil
|
return nil
|
||||||
@ -140,3 +149,16 @@ func (a *App) CloseMidiPort() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) PlayNote(msg MIDIMessage) error {
|
||||||
|
if out == nil {
|
||||||
|
return errors.New("failed to initialize MIDI output")
|
||||||
|
}
|
||||||
|
channelByte := byte(msg.Channel)
|
||||||
|
if msg.MessageType == "NoteOn" {
|
||||||
|
out.SendMessage([]byte{0x90 | channelByte, byte(msg.Note), byte(msg.Velocity)})
|
||||||
|
} else if msg.MessageType == "NoteOff" {
|
||||||
|
out.SendMessage([]byte{0x80 | channelByte, byte(msg.Note), byte(msg.Velocity)})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -12,13 +12,15 @@ export const AudiotrackButton: FC<{
|
|||||||
size?: 'small' | 'medium' | 'large',
|
size?: 'small' | 'medium' | 'large',
|
||||||
shape?: 'rounded' | 'circular' | 'square';
|
shape?: 'rounded' | 'circular' | 'square';
|
||||||
appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent';
|
appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent';
|
||||||
}> = ({ size, shape, appearance }) => {
|
setPrompt: (prompt: string) => void;
|
||||||
|
}> = ({ size, shape, appearance, setPrompt }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return <Dialog onOpenChange={(e, data) => {
|
return <Dialog onOpenChange={(e, data) => {
|
||||||
if (!data.open) {
|
if (!data.open) {
|
||||||
flushMidiRecordingContent();
|
flushMidiRecordingContent();
|
||||||
commonStore.setRecordingTrackId('');
|
commonStore.setRecordingTrackId('');
|
||||||
|
commonStore.setPlayingTrackId('');
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<DialogTrigger disableButtonEnhancement>
|
<DialogTrigger disableButtonEnhancement>
|
||||||
@ -30,7 +32,7 @@ export const AudiotrackButton: FC<{
|
|||||||
<DialogBody>
|
<DialogBody>
|
||||||
<DialogContent className="overflow-hidden">
|
<DialogContent className="overflow-hidden">
|
||||||
<CustomToastContainer />
|
<CustomToastContainer />
|
||||||
<LazyImportComponent lazyChildren={AudiotrackEditor} />
|
<LazyImportComponent lazyChildren={AudiotrackEditor} lazyProps={{ setPrompt }} />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</DialogBody>
|
</DialogBody>
|
||||||
</DialogSurface>
|
</DialogSurface>
|
||||||
|
@ -9,11 +9,13 @@ import {
|
|||||||
ArrowAutofitWidth20Regular,
|
ArrowAutofitWidth20Regular,
|
||||||
Delete16Regular,
|
Delete16Regular,
|
||||||
MusicNote220Regular,
|
MusicNote220Regular,
|
||||||
|
Pause16Regular,
|
||||||
|
Play16Filled,
|
||||||
Play16Regular,
|
Play16Regular,
|
||||||
Record16Regular,
|
Record16Regular,
|
||||||
Stop16Filled
|
Stop16Filled
|
||||||
} from '@fluentui/react-icons';
|
} from '@fluentui/react-icons';
|
||||||
import { Button, Card, Slider, Text, Tooltip } from '@fluentui/react-components';
|
import { Button, Card, DialogTrigger, Slider, Text, Tooltip } from '@fluentui/react-components';
|
||||||
import { useWindowSize } from 'usehooks-ts';
|
import { useWindowSize } from 'usehooks-ts';
|
||||||
import commonStore from '../../stores/commonStore';
|
import commonStore from '../../stores/commonStore';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
@ -26,6 +28,7 @@ import {
|
|||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { ToastOptions } from 'react-toastify/dist/types';
|
import { ToastOptions } from 'react-toastify/dist/types';
|
||||||
import { flushMidiRecordingContent, refreshTracksTotalTime } from '../../utils';
|
import { flushMidiRecordingContent, refreshTracksTotalTime } from '../../utils';
|
||||||
|
import { PlayNote } from '../../../wailsjs/go/backend_golang/App';
|
||||||
|
|
||||||
const snapValue = 25;
|
const snapValue = 25;
|
||||||
const minimalMoveTime = 8; // 1000/125=8ms wait_events=125
|
const minimalMoveTime = 8; // 1000/125=8ms wait_events=125
|
||||||
@ -109,9 +112,7 @@ const midiMessageToToken = (msg: MidiMessage) => {
|
|||||||
|
|
||||||
let dropRecordingTime = false;
|
let dropRecordingTime = false;
|
||||||
|
|
||||||
export const midiMessageHandler = (data: MidiMessage) => {
|
export const midiMessageHandler = async (data: MidiMessage) => {
|
||||||
if (data.messageType === 'NoteOff')
|
|
||||||
return;
|
|
||||||
if (data.messageType === 'ControlChange') {
|
if (data.messageType === 'ControlChange') {
|
||||||
commonStore.setInstrumentType(Math.round(data.value / 127 * (InstrumentTypeNameMap.length - 1)));
|
commonStore.setInstrumentType(Math.round(data.value / 127 * (InstrumentTypeNameMap.length - 1)));
|
||||||
displayCurrentInstrumentType();
|
displayCurrentInstrumentType();
|
||||||
@ -122,8 +123,15 @@ export const midiMessageHandler = (data: MidiMessage) => {
|
|||||||
dropRecordingTime = false;
|
dropRecordingTime = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
data = {
|
||||||
|
...data,
|
||||||
|
instrument: commonStore.instrumentType
|
||||||
|
};
|
||||||
commonStore.setRecordingRawContent([...commonStore.recordingRawContent, data]);
|
commonStore.setRecordingRawContent([...commonStore.recordingRawContent, data]);
|
||||||
commonStore.setRecordingContent(commonStore.recordingContent + midiMessageToToken(data));
|
commonStore.setRecordingContent(commonStore.recordingContent + midiMessageToToken(data));
|
||||||
|
|
||||||
|
//TODO data.channel = data.instrument;
|
||||||
|
PlayNote(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,7 +188,7 @@ const Track: React.FC<TrackProps> = observer(({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const AudiotrackEditor: FC = observer(() => {
|
const AudiotrackEditor: FC<{ setPrompt: (prompt: string) => void }> = observer(({ setPrompt }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const viewControlsContainerRef = useRef<HTMLDivElement>(null);
|
const viewControlsContainerRef = useRef<HTMLDivElement>(null);
|
||||||
@ -243,7 +251,7 @@ const AudiotrackEditor: FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex pb-2 border-b" ref={toolbarRef}>
|
<div className="flex pb-2 border-b" ref={toolbarRef}>
|
||||||
<div className="flex gap-2" ref={toolbarButtonRef}>
|
<div className="flex gap-2" ref={toolbarButtonRef}>
|
||||||
<ToolTipButton desc={t('Play All')} icon={<Play16Regular />} />
|
<ToolTipButton disabled desc={t('Play All') + ' (Unavailable)'} icon={<Play16Regular />} />
|
||||||
<ToolTipButton desc={t('Clear All')} icon={<Delete16Regular />} onClick={() => {
|
<ToolTipButton desc={t('Clear All')} icon={<Delete16Regular />} onClick={() => {
|
||||||
commonStore.setTracks([]);
|
commonStore.setTracks([]);
|
||||||
commonStore.setTrackScale(1);
|
commonStore.setTrackScale(1);
|
||||||
@ -348,9 +356,10 @@ const AudiotrackEditor: FC = observer(() => {
|
|||||||
<div className="flex gap-1 border-r h-7">
|
<div className="flex gap-1 border-r h-7">
|
||||||
<ToolTipButton desc={commonStore.recordingTrackId === track.id ? t('Stop') : t('Record')}
|
<ToolTipButton desc={commonStore.recordingTrackId === track.id ? t('Stop') : t('Record')}
|
||||||
icon={commonStore.recordingTrackId === track.id ? <Stop16Filled /> : <Record16Regular />}
|
icon={commonStore.recordingTrackId === track.id ? <Stop16Filled /> : <Record16Regular />}
|
||||||
size="small" shape="circular"
|
size="small" shape="circular" appearance="subtle"
|
||||||
appearance="subtle" onClick={() => {
|
onClick={() => {
|
||||||
flushMidiRecordingContent();
|
flushMidiRecordingContent();
|
||||||
|
commonStore.setPlayingTrackId('');
|
||||||
|
|
||||||
if (commonStore.recordingTrackId === track.id) {
|
if (commonStore.recordingTrackId === track.id) {
|
||||||
commonStore.setRecordingTrackId('');
|
commonStore.setRecordingTrackId('');
|
||||||
@ -363,8 +372,22 @@ const AudiotrackEditor: FC = observer(() => {
|
|||||||
commonStore.setRecordingRawContent(track.rawContent.slice());
|
commonStore.setRecordingRawContent(track.rawContent.slice());
|
||||||
}
|
}
|
||||||
}} />
|
}} />
|
||||||
<ToolTipButton desc={t('Play')} icon={<Play16Regular />} size="small" shape="circular"
|
<ToolTipButton disabled
|
||||||
appearance="subtle" />
|
desc={commonStore.playingTrackId === track.id ? t('Stop') : t('Play') + ' (Unavailable)'}
|
||||||
|
icon={commonStore.playingTrackId === track.id ? <Pause16Regular /> : <Play16Filled />}
|
||||||
|
size="small" shape="circular" appearance="subtle"
|
||||||
|
onClick={() => {
|
||||||
|
flushMidiRecordingContent();
|
||||||
|
commonStore.setRecordingTrackId('');
|
||||||
|
|
||||||
|
if (commonStore.playingTrackId === track.id) {
|
||||||
|
commonStore.setPlayingTrackId('');
|
||||||
|
} else {
|
||||||
|
setSelectedTrackId(track.id);
|
||||||
|
|
||||||
|
commonStore.setPlayingTrackId(track.id);
|
||||||
|
}
|
||||||
|
}} />
|
||||||
<ToolTipButton desc={t('Delete')} icon={<Delete16Regular />} size="small" shape="circular"
|
<ToolTipButton desc={t('Delete')} icon={<Delete16Regular />} size="small" shape="circular"
|
||||||
appearance="subtle" onClick={() => {
|
appearance="subtle" onClick={() => {
|
||||||
const tracks = commonStore.tracks.slice().filter(t => t.id !== track.id);
|
const tracks = commonStore.tracks.slice().filter(t => t.id !== track.id);
|
||||||
@ -416,9 +439,52 @@ const AudiotrackEditor: FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
}
|
}
|
||||||
<Button icon={<MusicNote220Regular />} style={{ minHeight: '32px' }}>
|
<DialogTrigger disableButtonEnhancement>
|
||||||
|
<Button icon={<MusicNote220Regular />} style={{ minHeight: '32px' }} onClick={() => {
|
||||||
|
flushMidiRecordingContent();
|
||||||
|
commonStore.setRecordingTrackId('');
|
||||||
|
commonStore.setPlayingTrackId('');
|
||||||
|
|
||||||
|
const timestamp = [];
|
||||||
|
const sortedTracks = commonStore.tracks.slice().sort((a, b) => a.offsetTime - b.offsetTime);
|
||||||
|
for (const track of sortedTracks) {
|
||||||
|
timestamp.push(track.offsetTime);
|
||||||
|
let accContentTime = 0;
|
||||||
|
for (const msg of track.rawContent) {
|
||||||
|
if (msg.messageType === 'ElapsedTime') {
|
||||||
|
accContentTime += msg.value;
|
||||||
|
timestamp.push(track.offsetTime + accContentTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sortedTimestamp = timestamp.slice().sort((a, b) => a - b);
|
||||||
|
const globalMessages: MidiMessage[] = sortedTimestamp.reduce((messages, current, i) =>
|
||||||
|
[...messages, {
|
||||||
|
messageType: 'ElapsedTime',
|
||||||
|
value: current - (i === 0 ? 0 : sortedTimestamp[i - 1])
|
||||||
|
} as MidiMessage]
|
||||||
|
, [] as MidiMessage[]);
|
||||||
|
for (const track of sortedTracks) {
|
||||||
|
let currentTime = track.offsetTime;
|
||||||
|
let accContentTime = 0;
|
||||||
|
for (const msg of track.rawContent) {
|
||||||
|
if (msg.messageType === 'ElapsedTime') {
|
||||||
|
accContentTime += msg.value;
|
||||||
|
currentTime = track.offsetTime + accContentTime;
|
||||||
|
} else if (msg.messageType === 'NoteOn') {
|
||||||
|
const insertIndex = sortedTimestamp.findIndex(t => t >= currentTime);
|
||||||
|
globalMessages.splice(insertIndex + 1, 0, msg);
|
||||||
|
sortedTimestamp.splice(insertIndex + 1, 0, 0); // placeholder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = globalMessages.map(m => midiMessageToToken(m)).join('');
|
||||||
|
commonStore.setCompositionSubmittedPrompt(result);
|
||||||
|
setPrompt(result);
|
||||||
|
}}>
|
||||||
{t('Save to generation area')}
|
{t('Save to generation area')}
|
||||||
</Button>
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,7 @@ import {
|
|||||||
OpenMidiPort,
|
OpenMidiPort,
|
||||||
OpenSaveFileDialogBytes
|
OpenSaveFileDialogBytes
|
||||||
} from '../../wailsjs/go/backend_golang/App';
|
} from '../../wailsjs/go/backend_golang/App';
|
||||||
import { getServerRoot, toastWithButton } from '../utils';
|
import { getServerRoot, getSoundFont, toastWithButton } from '../utils';
|
||||||
import { CompositionParams } from '../types/composition';
|
import { CompositionParams } from '../types/composition';
|
||||||
import { useMediaQuery } from 'usehooks-ts';
|
import { useMediaQuery } from 'usehooks-ts';
|
||||||
import { AudiotrackButton } from './AudiotrackManager/AudiotrackButton';
|
import { AudiotrackButton } from './AudiotrackManager/AudiotrackButton';
|
||||||
@ -71,20 +71,8 @@ const CompositionPanel: FC = observer(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const setSoundFont = async () => {
|
const setSoundFont = async () => {
|
||||||
let soundUrl: string;
|
|
||||||
if (commonStore.compositionParams.useLocalSoundFont)
|
|
||||||
soundUrl = 'assets/sound-font';
|
|
||||||
else
|
|
||||||
soundUrl = !commonStore.settings.giteeUpdatesSource ?
|
|
||||||
`https://raw.githubusercontent.com/josStorer/sgm_plus/master` :
|
|
||||||
`https://gitee.com/josc146/sgm_plus/raw/master`;
|
|
||||||
const fallbackUrl = 'https://cdn.jsdelivr.net/gh/josstorer/sgm_plus';
|
|
||||||
await fetch(soundUrl + '/soundfont.json').then(r => {
|
|
||||||
if (!r.ok)
|
|
||||||
soundUrl = fallbackUrl;
|
|
||||||
}).catch(() => soundUrl = fallbackUrl);
|
|
||||||
if (playerRef.current) {
|
if (playerRef.current) {
|
||||||
playerRef.current.soundFont = soundUrl;
|
playerRef.current.soundFont = await getSoundFont();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -302,7 +290,7 @@ const CompositionPanel: FC = observer(() => {
|
|||||||
<Option key={i} value={i.toString()}>{p.name}</Option>)
|
<Option key={i} value={i.toString()}>{p.name}</Option>)
|
||||||
}
|
}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
<AudiotrackButton />
|
<AudiotrackButton setPrompt={setPrompt} />
|
||||||
</div>
|
</div>
|
||||||
} />
|
} />
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,6 @@ async function initMidi() {
|
|||||||
commonStore.setMidiPorts(data);
|
commonStore.setMidiPorts(data);
|
||||||
});
|
});
|
||||||
EventsOn('midiMessage', async (data: MidiMessage) => {
|
EventsOn('midiMessage', async (data: MidiMessage) => {
|
||||||
(await import('./pages/AudiotrackManager/AudiotrackEditor')).midiMessageHandler(data);
|
await (await import('./pages/AudiotrackManager/AudiotrackEditor')).midiMessageHandler(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -107,8 +107,9 @@ class CommonStore {
|
|||||||
trackTotalTime: number = tracksMinimalTotalTime;
|
trackTotalTime: number = tracksMinimalTotalTime;
|
||||||
trackCurrentTime: number = 0;
|
trackCurrentTime: number = 0;
|
||||||
trackPlayStartTime: number = 0;
|
trackPlayStartTime: number = 0;
|
||||||
|
playingTrackId: string = '';
|
||||||
recordingTrackId: string = '';
|
recordingTrackId: string = '';
|
||||||
recordingContent: string = ''; // used to improve performance, and I'm too lazy to maintain an ID dictionary for this
|
recordingContent: string = ''; // used to improve performance of midiMessageHandler, and I'm too lazy to maintain an ID dictionary for this (although that would be better for realtime effects)
|
||||||
recordingRawContent: MidiMessage[] = [];
|
recordingRawContent: MidiMessage[] = [];
|
||||||
// configs
|
// configs
|
||||||
currentModelConfigIndex: number = 0;
|
currentModelConfigIndex: number = 0;
|
||||||
@ -444,6 +445,10 @@ class CommonStore {
|
|||||||
setRecordingRawContent(value: MidiMessage[]) {
|
setRecordingRawContent(value: MidiMessage[]) {
|
||||||
this.recordingRawContent = value;
|
this.recordingRawContent = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPlayingTrackId(value: string) {
|
||||||
|
this.playingTrackId = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new CommonStore();
|
export default new CommonStore();
|
@ -32,6 +32,7 @@ export type MidiMessage = {
|
|||||||
velocity: number;
|
velocity: number;
|
||||||
control: number;
|
control: number;
|
||||||
value: number;
|
value: number;
|
||||||
|
instrument: InstrumentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum InstrumentType {
|
export enum InstrumentType {
|
||||||
|
@ -511,6 +511,22 @@ export function flushMidiRecordingContent() {
|
|||||||
commonStore.setRecordingRawContent([]);
|
commonStore.setRecordingRawContent([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getSoundFont() {
|
||||||
|
let soundUrl: string;
|
||||||
|
if (commonStore.compositionParams.useLocalSoundFont)
|
||||||
|
soundUrl = 'assets/sound-font';
|
||||||
|
else
|
||||||
|
soundUrl = !commonStore.settings.giteeUpdatesSource ?
|
||||||
|
`https://raw.githubusercontent.com/josStorer/sgm_plus/master` :
|
||||||
|
`https://gitee.com/josc146/sgm_plus/raw/master`;
|
||||||
|
const fallbackUrl = 'https://cdn.jsdelivr.net/gh/josstorer/sgm_plus';
|
||||||
|
await fetch(soundUrl + '/soundfont.json').then(r => {
|
||||||
|
if (!r.ok)
|
||||||
|
soundUrl = fallbackUrl;
|
||||||
|
}).catch(() => soundUrl = fallbackUrl);
|
||||||
|
return soundUrl;
|
||||||
|
}
|
||||||
|
|
||||||
export function getSupportedCustomCudaFile(isBeta: boolean) {
|
export function getSupportedCustomCudaFile(isBeta: boolean) {
|
||||||
if ([' 10', ' 16', ' 20', ' 30', 'MX', 'Tesla P', 'Quadro P', 'NVIDIA P', 'TITAN X', 'TITAN RTX', 'RTX A',
|
if ([' 10', ' 16', ' 20', ' 30', 'MX', 'Tesla P', 'Quadro P', 'NVIDIA P', 'TITAN X', 'TITAN RTX', 'RTX A',
|
||||||
'Quadro RTX 4000', 'Quadro RTX 5000', 'Tesla T4', 'NVIDIA A10', 'NVIDIA A40'].some(v => commonStore.status.device_name.includes(v)))
|
'Quadro RTX 4000', 'Quadro RTX 5000', 'Tesla T4', 'NVIDIA A10', 'NVIDIA A40'].some(v => commonStore.status.device_name.includes(v)))
|
||||||
|
2
frontend/wailsjs/go/backend_golang/App.d.ts
generated
vendored
2
frontend/wailsjs/go/backend_golang/App.d.ts
generated
vendored
@ -48,6 +48,8 @@ export function OpenSaveFileDialogBytes(arg1:string,arg2:string,arg3:Array<numbe
|
|||||||
|
|
||||||
export function PauseDownload(arg1:string):Promise<void>;
|
export function PauseDownload(arg1:string):Promise<void>;
|
||||||
|
|
||||||
|
export function PlayNote(arg1:backend_golang.MIDIMessage):Promise<void>;
|
||||||
|
|
||||||
export function ReadFileInfo(arg1:string):Promise<backend_golang.FileInfo>;
|
export function ReadFileInfo(arg1:string):Promise<backend_golang.FileInfo>;
|
||||||
|
|
||||||
export function ReadJson(arg1:string):Promise<any>;
|
export function ReadJson(arg1:string):Promise<any>;
|
||||||
|
4
frontend/wailsjs/go/backend_golang/App.js
generated
4
frontend/wailsjs/go/backend_golang/App.js
generated
@ -94,6 +94,10 @@ export function PauseDownload(arg1) {
|
|||||||
return window['go']['backend_golang']['App']['PauseDownload'](arg1);
|
return window['go']['backend_golang']['App']['PauseDownload'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function PlayNote(arg1) {
|
||||||
|
return window['go']['backend_golang']['App']['PlayNote'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function ReadFileInfo(arg1) {
|
export function ReadFileInfo(arg1) {
|
||||||
return window['go']['backend_golang']['App']['ReadFileInfo'](arg1);
|
return window['go']['backend_golang']['App']['ReadFileInfo'](arg1);
|
||||||
}
|
}
|
||||||
|
22
frontend/wailsjs/go/models.ts
generated
22
frontend/wailsjs/go/models.ts
generated
@ -18,6 +18,28 @@ export namespace backend_golang {
|
|||||||
this.modTime = source["modTime"];
|
this.modTime = source["modTime"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class MIDIMessage {
|
||||||
|
messageType: string;
|
||||||
|
channel: number;
|
||||||
|
note: number;
|
||||||
|
velocity: number;
|
||||||
|
control: number;
|
||||||
|
value: number;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new MIDIMessage(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.messageType = source["messageType"];
|
||||||
|
this.channel = source["channel"];
|
||||||
|
this.note = source["note"];
|
||||||
|
this.velocity = source["velocity"];
|
||||||
|
this.control = source["control"];
|
||||||
|
this.value = source["value"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user