Add boilerplate files and licenses
42
src/App.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { Provider } from 'jotai';
|
||||
import { DndProvider } from 'react-dnd/dist/core/DndProvider';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { Route, Switch } from 'wouter-preact';
|
||||
import Faq from '~/routes/faq/Faq';
|
||||
import Home from '~/routes/home/Home';
|
||||
import DeviceProvider from './components/DeviceProvider';
|
||||
import ErrorBoundary from './components/ErrorBoundary';
|
||||
import ErrorFallback from './components/ErrorFallback';
|
||||
import Layout from './components/Layout';
|
||||
import Page404 from './components/Page404';
|
||||
import queryClient from './lib/queryClient';
|
||||
import { store } from './lib/store';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||||
<Provider store={store}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Layout>
|
||||
<DeviceProvider>
|
||||
<Switch>
|
||||
<Route path="/">
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Home />
|
||||
</DndProvider>
|
||||
</Route>
|
||||
<Route path="/faq">
|
||||
<Faq />
|
||||
</Route>
|
||||
<Route component={Page404} />
|
||||
</Switch>
|
||||
</DeviceProvider>
|
||||
</Layout>
|
||||
</QueryClientProvider>
|
||||
</Provider>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
4
src/atoms/deviceSku.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { atom } from 'jotai';
|
||||
import { SKU_EP133 } from '~/lib/constants';
|
||||
|
||||
export const deviceSkuAtom = atom<string>(SKU_EP133);
|
||||
13
src/atoms/droppedProjectFile.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { atom } from 'jotai';
|
||||
import type JSZip from 'jszip';
|
||||
|
||||
export type DroppedProjectFile = {
|
||||
name: string;
|
||||
data: Uint8Array;
|
||||
};
|
||||
|
||||
export const droppedProjectFileAtom = atom<DroppedProjectFile | null>(null);
|
||||
export const droppedBackupFileAtom = atom<Uint8Array | null>(null);
|
||||
export const unzippedBackupAtom = atom<JSZip | null>(null);
|
||||
export const backupSkuAtom = atom<string | null>(null);
|
||||
export const backupProjectIdsAtom = atom<number[]>([]);
|
||||
3
src/atoms/feedbackDialog.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
export const feedbackDialogAtom = atom<boolean>(false);
|
||||
4
src/atoms/project.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
export const projectIdAtom = atom<string>('');
|
||||
export const customSceneNamesByProjectAtom = atom<Record<string, boolean>>({});
|
||||
3
src/atoms/swUpdate.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
export const swUpdateAvailableAtom = atom(false);
|
||||
19
src/atoms/toast.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { atom } from 'jotai';
|
||||
|
||||
export type ToastSeverity = 'info' | 'error';
|
||||
|
||||
export interface ToastMessage {
|
||||
id: string;
|
||||
message: string;
|
||||
severity: ToastSeverity;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
export const toastsAtom = atom<ToastMessage[]>([]);
|
||||
|
||||
export const removeToastAtom = atom(null, (get, set, id: string) => {
|
||||
set(
|
||||
toastsAtom,
|
||||
get(toastsAtom).filter((toast) => toast.id !== id),
|
||||
);
|
||||
});
|
||||
12
src/components/DeviceContext.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createContext } from 'preact';
|
||||
import { TEDevice } from '../lib/midi/types';
|
||||
|
||||
const DeviceContext = createContext<{
|
||||
device: TEDevice | null;
|
||||
error: Error | null;
|
||||
}>({
|
||||
device: null,
|
||||
error: null,
|
||||
});
|
||||
|
||||
export default DeviceContext;
|
||||
40
src/components/DeviceProvider.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useAtom } from 'jotai';
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { deviceSkuAtom } from '~/atoms/deviceSku';
|
||||
import { SKU_EP133 } from '~/lib/constants';
|
||||
import { initDevice } from '../lib/midi';
|
||||
import { TEDevice } from '../lib/midi/types';
|
||||
import DeviceContext from './DeviceContext';
|
||||
|
||||
function DeviceProvider({ children }: { children: preact.ComponentChildren }) {
|
||||
const [device, setDevice] = useState<TEDevice | null>(null);
|
||||
const [deviceError, setDeviceError] = useState<Error | null>(null);
|
||||
const [, setDeviceSku] = useAtom(deviceSkuAtom);
|
||||
|
||||
useEffect(() => {
|
||||
initDevice({
|
||||
onDeviceFound: (deviceInfo) => {
|
||||
console.log('Device found:', deviceInfo);
|
||||
setDevice(deviceInfo);
|
||||
setDeviceSku(deviceInfo.sku);
|
||||
},
|
||||
onDeviceLost: () => {
|
||||
console.log('Device lost');
|
||||
setDevice(null);
|
||||
setDeviceSku(SKU_EP133);
|
||||
},
|
||||
onNoMidiAccess: (error) => {
|
||||
console.error('No MIDI access:', error);
|
||||
setDeviceError(error);
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DeviceContext.Provider value={{ device, error: deviceError }}>
|
||||
{children}
|
||||
</DeviceContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default DeviceProvider;
|
||||
41
src/components/ErrorBoundary.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Component } from 'preact';
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: any;
|
||||
FallbackComponent: any;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
hasError: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
state: ErrorBoundaryState;
|
||||
|
||||
constructor(props: ErrorBoundaryProps) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: any) {
|
||||
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
||||
Sentry.captureReactException(error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError && this.state.error) {
|
||||
const FallbackComponent = this.props.FallbackComponent;
|
||||
return <FallbackComponent error={this.state.error} />;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
22
src/components/ErrorFallback.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import Button from './ui/Button';
|
||||
|
||||
function ErrorFallback() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen p-4">
|
||||
<div className="max-w-md w-full bg-white border-1 border-black p-6 flex flex-col gap-4">
|
||||
<h1 className="text-xl font-medium">Something went wrong</h1>
|
||||
|
||||
<div className="mb-4 text-sm text-red-700 mb-2">
|
||||
An unexpected error occurred. Please try refreshing the page. If the problem persists,
|
||||
please submit a bug report. There is a feedback button in the top right corner of the app.
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button onClick={() => window.location.reload()}>Refresh Page</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ErrorFallback;
|
||||
12
src/components/Layout.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import useTheme from '~/hooks/useTheme';
|
||||
|
||||
function Layout({ children }: { children: preact.ComponentChildren }) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<div className={`${theme.id} h-full w-full bg-(image:--bg-url) fixed overflow-y-auto`}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
34
src/components/OfflineInformer.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
|
||||
function OfflineInformer({ className }: { className?: string }) {
|
||||
const [isOnline, setIsOnline] = useState(navigator.onLine);
|
||||
|
||||
useEffect(() => {
|
||||
const handleOnline = () => setIsOnline(true);
|
||||
const handleOffline = () => setIsOnline(false);
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (isOnline) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'bg-yellow-100 text-yellow-800 px-4 py-1 text-center text-sm border-b border-yellow-300',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
You’re currently offline, but the app is still working just fine!
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default OfflineInformer;
|
||||
12
src/components/Page404.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
function Page404() {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-dvh p-4">
|
||||
<div className="p-4 border border-black bg-white text-center max-w-md">
|
||||
<h1 className="text-2xl mb-4">404: Not Found</h1>
|
||||
<p>The page you are looking for does not exist.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page404;
|
||||
47
src/components/UpdateNotifier.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import clsx from 'clsx';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useEffect, useState } from 'preact/hooks';
|
||||
import { swUpdateAvailableAtom } from '~/atoms/swUpdate';
|
||||
|
||||
const RELOAD_DELAY_SECONDS = 3;
|
||||
|
||||
function UpdateNotifier({ className }: { className?: string }) {
|
||||
const updateAvailable = useAtomValue(swUpdateAvailableAtom);
|
||||
const [countdown, setCountdown] = useState(RELOAD_DELAY_SECONDS);
|
||||
|
||||
useEffect(() => {
|
||||
if (!updateAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setCountdown((prev) => {
|
||||
if (prev <= 1) {
|
||||
clearInterval(interval);
|
||||
window.location.reload();
|
||||
return 0;
|
||||
}
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [updateAvailable]);
|
||||
|
||||
if (!updateAvailable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'bg-blue-200 text-blue-800 px-4 py-1 text-center text-sm border-b border-blue-300',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
A new version is available. Reloading in {countdown} second{countdown !== 1 ? 's' : ''}...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UpdateNotifier;
|
||||
37
src/components/form/CheckboxField.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Controller, FieldPath, FieldValues, useFormContext } from 'react-hook-form';
|
||||
import CheckBox from '../ui/CheckBox';
|
||||
|
||||
function CheckboxField<T extends FieldValues = FieldValues>({
|
||||
name,
|
||||
title,
|
||||
disabled,
|
||||
helperText,
|
||||
className,
|
||||
}: {
|
||||
name: FieldPath<T>;
|
||||
title: string;
|
||||
disabled?: boolean;
|
||||
helperText?: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const { control } = useFormContext<T>();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<CheckBox
|
||||
checked={field.value as boolean}
|
||||
onChange={(checked) => field.onChange(checked)}
|
||||
title={title}
|
||||
disabled={disabled ?? false}
|
||||
helperText={helperText}
|
||||
className={className}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default CheckboxField;
|
||||
42
src/components/form/InputField.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Controller, FieldPath, FieldValues, useFormContext } from 'react-hook-form';
|
||||
import Input from '../ui/Input';
|
||||
|
||||
function InputField<T extends FieldValues = FieldValues>({
|
||||
name,
|
||||
label,
|
||||
disabled,
|
||||
className,
|
||||
placeholder,
|
||||
transform,
|
||||
}: {
|
||||
name: FieldPath<T>;
|
||||
label?: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
transform?: (value: string) => string;
|
||||
}) {
|
||||
const { control } = useFormContext<T>();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
label={label}
|
||||
value={field.value as string}
|
||||
onChange={(e: Event) => {
|
||||
const raw = (e.currentTarget as HTMLInputElement).value;
|
||||
field.onChange(transform ? transform(raw) : raw);
|
||||
}}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default InputField;
|
||||
39
src/components/form/SelectField.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { JSX } from 'preact';
|
||||
import { Controller, FieldPath, FieldValues, useFormContext } from 'react-hook-form';
|
||||
import Select from '../ui/Select';
|
||||
|
||||
function SelectField<T extends FieldValues = FieldValues>({
|
||||
name,
|
||||
children,
|
||||
disabled,
|
||||
className,
|
||||
}: {
|
||||
name: FieldPath<T>;
|
||||
children: any;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const { control } = useFormContext<T>();
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name={name}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
onChange={(e: JSX.TargetedEvent<HTMLSelectElement, Event>) =>
|
||||
field.onChange(e.currentTarget.value)
|
||||
}
|
||||
value={field.value as string}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default SelectField;
|
||||
6
src/components/icons/arrow-dialog.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="14 3 21 3 21 10"/>
|
||||
<line x1="10" y1="14" x2="21" y2="3"/>
|
||||
<path d="M21 21H5a2 2 0 0 1-2-2V5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 307 B |
5
src/components/icons/arrow-left.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="19" y1="12" x2="5" y2="12" />
|
||||
<polyline points="12 19 5 12 12 5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 270 B |
1
src/components/icons/check.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z"/></svg>
|
||||
|
After Width: | Height: | Size: 157 B |
1
src/components/icons/close.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z"/></svg>
|
||||
|
After Width: | Height: | Size: 201 B |
1
src/components/icons/edit.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="M200-200h57l391-391-57-57-391 391v57Zm-80 80v-170l528-527q12-11 26.5-17t30.5-6q16 0 31 6t26 18l55 56q12 11 17.5 26t5.5 30q0 16-5.5 30.5T817-647L290-120H120Zm640-584-56-56 56 56Zm-141 85-28-29 57 57-29-28Z"/></svg>
|
||||
|
After Width: | Height: | Size: 308 B |
4
src/components/icons/file.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<path d="M 6 3 L 6 4 L 6 28 L 6 29 L 7 29 L 25 29 L 26 29 L 26 28 L 26 10 L 26 9.59375 L 25.71875 9.28125 L 19.71875 3.28125 L 19.40625 3 L 19 3 L 7 3 L 6 3 z M 8 5 L 18 5 L 18 10 L 18 11 L 19 11 L 24 11 L 24 27 L 8 27 L 8 5 z M 20 6.4375 L 22.5625 9 L 20 9 L 20 6.4375 z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 370 B |
8
src/components/icons/github.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 814 B |
5
src/components/icons/info.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<line x1="12" y1="16" x2="12" y2="12"/>
|
||||
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 286 B |
8
src/components/icons/mail.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg height="24px" width="24px" version="1.1" viewBox="0 0 512 512" >
|
||||
<g>
|
||||
<path d="M0,64v384h512V64H0z M264.132,266.765c-1.996,1.688-4.95,2.657-8.1,2.657c-3.153,0-6.098-0.964-8.083-2.642
|
||||
L48,112h416L264.132,266.765z M95.11,194.306l69.518,58.954L48,368V160L95.11,194.306z M199.288,282.652l16.35,13.866
|
||||
c10.836,9.215,25.169,14.288,40.361,14.288c15.236,0,29.588-5.071,40.418-14.282l16.321-13.846L432,400H80L199.288,282.652z
|
||||
M347.394,253.282L464,160v208L347.394,253.282z" fill="currentColor"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 518 B |
1
src/components/icons/music.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg>
|
||||
|
After Width: | Height: | Size: 253 B |
5
src/components/icons/reload.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="23 4 23 10 17 10"/>
|
||||
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 278 B |
10
src/components/icons/reset-settings.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="17.202343" height="14.702343" viewBox="0 0 17.202343 14.702343" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="m2.7515625.15000007c-1.4294397 0-2.60156255 1.16821683-2.60156255 2.59765633v9.2031246c0 1.42944 1.17212285 2.601563 2.60156255 2.601563h5.4511719V13.251563H2.7515625c-.7317272 0-1.3007812-.569054-1.3007812-1.300782V2.7476564c0-.7317273.569054-1.2988281 1.3007812-1.2988282H14.452734c.731728 0 1.298829.5671009 1.298829 1.2988282v2.9550781h1.300781V2.7476564c0-1.4294395-1.17017-2.59765633-2.59961-2.59765633z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<g stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" transform="translate(-8.6914339 -2.1223497)">
|
||||
<line x1="19.643778" y1="10.574694" x2="25.143778" y2="16.074694" />
|
||||
<line x1="25.143778" y1="10.574694" x2="19.643778" y2="16.074694" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 878 B |
8
src/components/icons/trash.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 6h18"/>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 394 B |
68
src/components/ui/Button.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import clsx from 'clsx';
|
||||
import { JSX } from 'preact';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { Link } from 'wouter';
|
||||
|
||||
interface ButtonProps extends JSX.HTMLAttributes<HTMLButtonElement> {
|
||||
children: any;
|
||||
size?: 'sm' | 'md' | 'xs';
|
||||
disabled?: boolean;
|
||||
variant?: 'primary' | 'secondary' | 'outlined' | 'tertiary' | 'ghost' | 'icon';
|
||||
type?: 'button' | 'submit' | 'reset';
|
||||
to?: string;
|
||||
target?: string;
|
||||
rel?: string;
|
||||
}
|
||||
|
||||
function Button({
|
||||
children,
|
||||
className = '',
|
||||
variant = 'primary',
|
||||
size = 'md',
|
||||
type = 'button',
|
||||
to,
|
||||
target,
|
||||
rel,
|
||||
disabled,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
const variantClasses = {
|
||||
primary: 'bg-face hover:bg-[#d0d2d0] active:bg-[#c5c7c5]',
|
||||
secondary: 'bg-gray-100 hover:bg-gray-300 active:bg-gray-400',
|
||||
tertiary: 'bg-[#c6d1d7] hover:bg-gray-100 active:bg-gray-200',
|
||||
outlined: 'bg-transparent hover:bg-black/10 active:bg-black/20 border-1 border-black',
|
||||
ghost: 'border-0 bg-transparent hover:bg-black/10 active:bg-black/20',
|
||||
icon: 'flex-shrink-0 !py-1 !px-1 border-0 !h-auto',
|
||||
} as const;
|
||||
|
||||
const sizeClasses = {
|
||||
xs: 'text-xs py-1 px-2 h-[24px]',
|
||||
sm: 'text-sm py-1 px-2 h-[28px]',
|
||||
md: 'text-sm py-2 px-4 h-[42px]',
|
||||
} as const;
|
||||
|
||||
const baseClasses =
|
||||
'text-black cursor-pointer border-1 border-black disabled:opacity-80 disabled:cursor-not-allowed disabled:text-gray-400 outline-none transition-colors duration-200';
|
||||
|
||||
const linkDisabledClasses = disabled ? 'pointer-events-none opacity-80 text-gray-400' : '';
|
||||
|
||||
const composed = twMerge(
|
||||
clsx(baseClasses, variantClasses[variant], sizeClasses[size], linkDisabledClasses, className),
|
||||
);
|
||||
|
||||
if (to) {
|
||||
return (
|
||||
<Link href={to} className={composed} target={target} rel={rel} aria-disabled={disabled}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<button type={type} className={composed} disabled={disabled} {...props}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
||||
56
src/components/ui/CheckBox.tsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import clsx from 'clsx';
|
||||
import { JSX } from 'preact';
|
||||
import { useId } from 'preact/hooks';
|
||||
import IconInfo from '../icons/info.svg?react';
|
||||
|
||||
function CheckBox({
|
||||
checked,
|
||||
onChange,
|
||||
title,
|
||||
helperText,
|
||||
disabled,
|
||||
className,
|
||||
}: {
|
||||
checked: boolean;
|
||||
onChange: (checked: boolean) => void;
|
||||
title: string;
|
||||
helperText?: string;
|
||||
disabled: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
const _id = useId();
|
||||
|
||||
return (
|
||||
<label className={clsx('flex items-center gap-2 text-sm', className)} htmlFor={_id}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id={_id}
|
||||
checked={checked}
|
||||
onChange={(e: JSX.TargetedEvent<HTMLInputElement>) => onChange(e.currentTarget.checked)}
|
||||
disabled={disabled}
|
||||
className={clsx('w-4 h-4', disabled && 'opacity-50')}
|
||||
/>
|
||||
<span className={clsx('overflow-hidden truncate', disabled && 'opacity-50')} title={title}>
|
||||
{title}
|
||||
</span>
|
||||
|
||||
{helperText && (
|
||||
<div className="relative">
|
||||
<div className="text-gray-400 cursor-help peer">
|
||||
<IconInfo className="w-4 h-4" />
|
||||
</div>
|
||||
<div
|
||||
className={clsx(
|
||||
'absolute shadow-lg bottom-full left-1/2 -translate-x-1/2 mb-2 p-3 bg-[#b0babe] text-black border border-black text-sm z-2',
|
||||
'opacity-0 peer-hover:opacity-100 transition-opacity duration-300 pointer-events-none min-w-125 max-w-150',
|
||||
)}
|
||||
>
|
||||
{helperText}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export default CheckBox;
|
||||
51
src/components/ui/Dialog.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useRef } from 'preact/hooks';
|
||||
|
||||
interface DialogProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
children: any;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
}
|
||||
|
||||
function Dialog({
|
||||
isOpen,
|
||||
onClose,
|
||||
children,
|
||||
className = '',
|
||||
containerClassName = '',
|
||||
}: DialogProps) {
|
||||
const dialogRef = useRef<HTMLDialogElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const dialog = dialogRef.current;
|
||||
|
||||
if (!dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isOpen) {
|
||||
dialog.showModal();
|
||||
} else {
|
||||
dialog.close();
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<dialog
|
||||
ref={dialogRef}
|
||||
className={clsx(
|
||||
'shadow-lg backdrop:bg-black/40 fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 border border-black outline-none overflow-visible',
|
||||
className,
|
||||
)}
|
||||
onClose={onClose}
|
||||
>
|
||||
<div className={clsx('bg-white p-4 rounded-md min-w-75 min-h-25 max-h-[95vh] overflow-y-auto', containerClassName)}>
|
||||
{children}
|
||||
</div>
|
||||
</dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default Dialog;
|
||||
84
src/components/ui/FileInput.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
import { useRef } from 'preact/hooks';
|
||||
import IconFile from '../icons/file.svg?react';
|
||||
import IconTrash from '../icons/trash.svg?react';
|
||||
import Button from './Button';
|
||||
|
||||
interface FileInputProps {
|
||||
files: File[];
|
||||
onFilesChange: (files: File[]) => void;
|
||||
maxFiles?: number;
|
||||
disabled?: boolean;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
function FileInput({
|
||||
files,
|
||||
onFilesChange,
|
||||
maxFiles = 3,
|
||||
disabled = false,
|
||||
label = 'Attach files',
|
||||
}: FileInputProps) {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleFileSelect = (e: Event) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
if (target.files) {
|
||||
const newFiles = Array.from(target.files);
|
||||
onFilesChange([...files, ...newFiles].slice(0, maxFiles));
|
||||
}
|
||||
};
|
||||
|
||||
const removeFile = (index: number) => {
|
||||
onFilesChange(files.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
const openFileDialog = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-sm font-medium">
|
||||
{label} (optional, max {maxFiles})
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{files.map((file, index) => (
|
||||
<div
|
||||
key={`${file.name}-${index}`}
|
||||
className="flex gap-2 items-center p-2 bg-gray-50 rounded"
|
||||
>
|
||||
<IconFile className="w-6 h-6 flex-shrink-0" />
|
||||
<span className="text-sm truncate flex-1 max-w-[500px]">{file.name}</span>
|
||||
<span className="text-xs opacity-60 flex-shrink-0 ml-auto">
|
||||
{(file.size / 1024).toFixed(2)}KB
|
||||
</span>
|
||||
<Button variant="icon" onClick={() => removeFile(index)} disabled={disabled}>
|
||||
<IconTrash className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
{files.length < maxFiles && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={openFileDialog}
|
||||
className="flex items-center gap-2 w-fit"
|
||||
disabled={disabled}
|
||||
>
|
||||
<span className="text-2xl">+</span>
|
||||
<span className="text-sm">Add file</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
multiple
|
||||
onChange={handleFileSelect}
|
||||
className="hidden"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FileInput;
|
||||
66
src/components/ui/Input.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import clsx from 'clsx';
|
||||
import { JSX } from 'preact';
|
||||
import { forwardRef } from 'preact/compat';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
interface BaseInputProps {
|
||||
label?: string;
|
||||
type?: 'text' | 'email' | 'textarea' | 'file';
|
||||
error?: string;
|
||||
className?: string;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
onChange?: (e: Event) => void;
|
||||
required?: boolean;
|
||||
multiple?: boolean;
|
||||
accept?: string;
|
||||
placeholder?: string;
|
||||
rows?: number;
|
||||
}
|
||||
|
||||
type InputProps = BaseInputProps &
|
||||
(JSX.HTMLAttributes<HTMLInputElement> | JSX.HTMLAttributes<HTMLTextAreaElement>);
|
||||
|
||||
const Input = forwardRef<HTMLInputElement | HTMLTextAreaElement, InputProps>(
|
||||
({ label, type = 'text', className = '', disabled = false, ...props }, ref) => {
|
||||
const id = props.id || label?.toLowerCase().replace(/\s+/g, '-');
|
||||
const inputClasses = twMerge(
|
||||
clsx(
|
||||
'w-full px-3 py-2 border-1 border-black focus:outline-none focus:ring-0 bg-gray-50 placeholder:text-gray-400',
|
||||
disabled && 'opacity-50',
|
||||
className,
|
||||
),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
{label && (
|
||||
<label
|
||||
className={clsx('text-sm font-medium text-black', disabled && 'opacity-50')}
|
||||
htmlFor={id}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
{type === 'textarea' ? (
|
||||
<textarea
|
||||
ref={ref as preact.Ref<HTMLTextAreaElement>}
|
||||
className={inputClasses}
|
||||
id={id}
|
||||
{...(props as JSX.HTMLAttributes<HTMLTextAreaElement>)}
|
||||
/>
|
||||
) : (
|
||||
<input
|
||||
ref={ref as preact.Ref<HTMLInputElement>}
|
||||
type={type}
|
||||
className={inputClasses}
|
||||
id={id}
|
||||
{...(props as JSX.HTMLAttributes<HTMLInputElement>)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default Input;
|
||||
60
src/components/ui/Select.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import clsx from 'clsx';
|
||||
import { JSX } from 'preact';
|
||||
|
||||
interface SelectProps extends Omit<JSX.SelectHTMLAttributes<HTMLSelectElement>, 'size'> {
|
||||
children: any;
|
||||
className?: string;
|
||||
size?: 'sm' | 'md';
|
||||
variant?: 'primary' | 'secondary' | 'outlined' | 'tertiary' | 'ghost' | 'icon';
|
||||
}
|
||||
|
||||
function Select({
|
||||
children,
|
||||
className = '',
|
||||
size = 'md',
|
||||
variant = 'primary',
|
||||
...props
|
||||
}: SelectProps) {
|
||||
const variantClasses = {
|
||||
primary: 'bg-gray-100 hover:bg-[#d0d2d0] active:bg-[#c5c7c5] border-1 border-black',
|
||||
secondary: 'bg-gray-100 hover:bg-gray-300 active:bg-gray-400 border-1 border-black',
|
||||
tertiary: 'bg-[#c6d1d7] hover:bg-gray-100 active:bg-gray-200 border-1 border-black',
|
||||
outlined: 'bg-transparent hover:bg-black/10 active:bg-black/20 border-1 border-black',
|
||||
ghost: 'border-0 bg-transparent hover:bg-black/10 active:bg-black/20',
|
||||
icon: 'border-0 bg-transparent',
|
||||
} as const;
|
||||
|
||||
const sizeClasses = {
|
||||
sm: {
|
||||
wrapper: 'h-[28px]',
|
||||
select: 'relative h-[28px] px-1 py-0 text-sm -top-[1px]',
|
||||
},
|
||||
md: {
|
||||
wrapper: 'h-[42px]',
|
||||
select: 'h-[42px] p-2 text-sm',
|
||||
},
|
||||
} as const;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
'px-2 transition-colors duration-200',
|
||||
variantClasses[variant],
|
||||
sizeClasses[size].wrapper,
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<select
|
||||
className={clsx(
|
||||
'outline-none disabled:opacity-80 disabled:cursor-not-allowed disabled:text-gray-400 w-full cursor-pointer bg-transparent',
|
||||
sizeClasses[size].select,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Select;
|
||||
64
src/components/ui/Toast.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import clsx from 'clsx';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useEffect, useRef } from 'preact/hooks';
|
||||
import { removeToastAtom, type ToastMessage, toastsAtom } from '../../atoms/toast';
|
||||
import Button from './Button';
|
||||
|
||||
const TOAST_DURATION = 5000;
|
||||
|
||||
function ToastItem({ toast, onRemove }: { toast: ToastMessage; onRemove: (id: string) => void }) {
|
||||
const toastRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (toastRef.current) {
|
||||
toastRef.current.showPopover();
|
||||
}
|
||||
}, [toast.id]);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
onRemove(toast.id);
|
||||
}, TOAST_DURATION);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}, [toast.id, onRemove]);
|
||||
|
||||
return (
|
||||
<div ref={toastRef} popover>
|
||||
<div
|
||||
className={clsx(
|
||||
'flex gap-8 items-center px-4 py-3 shadow-md max-w-125 w-full text-black/90 border border-gray-800',
|
||||
'transform transition-all duration-300 ease-out',
|
||||
toast.severity === 'error' ? 'bg-red-500' : 'bg-[#7af07e]',
|
||||
)}
|
||||
>
|
||||
<p className="flex-1">{toast.message}</p>
|
||||
<Button variant="primary" size="sm" onClick={() => onRemove(toast.id)} className="ml-auto">
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Toast() {
|
||||
const [toasts] = useAtom(toastsAtom);
|
||||
const [, removeToast] = useAtom(removeToastAtom);
|
||||
|
||||
return (
|
||||
<>
|
||||
{toasts.map((toast) => (
|
||||
<ToastItem key={toast.id} toast={toast} onRemove={removeToast} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Toast;
|
||||
75
src/hooks/useAppState.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'preact/hooks';
|
||||
import { droppedBackupFileAtom } from '~/atoms/droppedProjectFile';
|
||||
import { projectIdAtom } from '../atoms/project';
|
||||
import useDevice from './useDevice';
|
||||
import useProject from './useProject';
|
||||
|
||||
export const APP_STATES = {
|
||||
CAN_DISPLAY_PROJECT: 'CAN_DISPLAY_PROJECT',
|
||||
NO_DEVICE_CONNECTED: 'NO_DEVICE_CONNECTED',
|
||||
ERROR: 'ERROR',
|
||||
LOADING: 'LOADING',
|
||||
NO_PROJECT_SELECTED: 'NO_PROJECT_SELECTED',
|
||||
CAN_SELECT_PROJECT: 'CAN_SELECT_PROJECT',
|
||||
CAN_RELOAD_PROJECT: 'CAN_RELOAD_PROJECT',
|
||||
DEVICE_READY: 'DEVICE_READY',
|
||||
CAN_EXPORT_PROJECT: 'CAN_EXPORT_PROJECT',
|
||||
HAS_BACKUP_FILE: 'HAS_BACKUP_FILE',
|
||||
};
|
||||
|
||||
export function useAppState() {
|
||||
const projectId = useAtomValue(projectIdAtom);
|
||||
const { isLoading: isLoadingProject, isRefetching } = useProject(projectId);
|
||||
const { device, error } = useDevice();
|
||||
const droppedBackupFile = useAtomValue(droppedBackupFileAtom);
|
||||
|
||||
const isLoading = isLoadingProject || isRefetching;
|
||||
const noDevice = !device;
|
||||
const hasBackupFile = !!droppedBackupFile;
|
||||
|
||||
return useMemo(() => {
|
||||
const finalState = [];
|
||||
|
||||
if (hasBackupFile) {
|
||||
finalState.push(APP_STATES.HAS_BACKUP_FILE);
|
||||
}
|
||||
|
||||
if (projectId && !isLoading && (!noDevice || hasBackupFile)) {
|
||||
finalState.push(APP_STATES.CAN_DISPLAY_PROJECT);
|
||||
}
|
||||
|
||||
if (noDevice && !error && !hasBackupFile) {
|
||||
finalState.push(APP_STATES.NO_DEVICE_CONNECTED);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
finalState.push(APP_STATES.ERROR);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
finalState.push(APP_STATES.LOADING);
|
||||
}
|
||||
|
||||
if (!projectId && !isLoading && (!noDevice || hasBackupFile)) {
|
||||
finalState.push(APP_STATES.NO_PROJECT_SELECTED);
|
||||
}
|
||||
|
||||
if (!noDevice && !isLoading) {
|
||||
finalState.push(APP_STATES.CAN_SELECT_PROJECT);
|
||||
}
|
||||
|
||||
if (hasBackupFile && !isLoading) {
|
||||
finalState.push(APP_STATES.CAN_SELECT_PROJECT);
|
||||
}
|
||||
|
||||
if (projectId && (!noDevice || hasBackupFile) && !isLoading) {
|
||||
if (!hasBackupFile) {
|
||||
finalState.push(APP_STATES.CAN_RELOAD_PROJECT);
|
||||
}
|
||||
finalState.push(APP_STATES.CAN_EXPORT_PROJECT);
|
||||
}
|
||||
|
||||
return finalState;
|
||||
}, [isLoading, noDevice, projectId, error, hasBackupFile]);
|
||||
}
|
||||
46
src/hooks/useCustomSceneNames.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { useAtom } from 'jotai';
|
||||
import { useCallback } from 'preact/hooks';
|
||||
import { customSceneNamesByProjectAtom } from '~/atoms/project';
|
||||
|
||||
function checkHasCustomSceneNames(projectPrefix: string): boolean {
|
||||
if (projectPrefix === 'p') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return Object.keys(localStorage).some((key) => key.startsWith(projectPrefix));
|
||||
} catch (error) {
|
||||
console.warn(`Error checking custom scene names:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function useCustomSceneNames(projectId: string) {
|
||||
const [customSceneNamesByProject, setCustomSceneNamesByProject] = useAtom(
|
||||
customSceneNamesByProjectAtom,
|
||||
);
|
||||
const projectPrefix = `p${projectId}`;
|
||||
const hasCustomNames =
|
||||
customSceneNamesByProject[projectId] ?? checkHasCustomSceneNames(projectPrefix);
|
||||
|
||||
const syncCustomSceneNames = useCallback(() => {
|
||||
setCustomSceneNamesByProject((prev) => ({
|
||||
...prev,
|
||||
[projectId]: checkHasCustomSceneNames(projectPrefix),
|
||||
}));
|
||||
}, [projectId, projectPrefix, setCustomSceneNamesByProject]);
|
||||
|
||||
const setHasCustomNames = useCallback(
|
||||
(value: boolean) => {
|
||||
setCustomSceneNamesByProject((prev) => ({
|
||||
...prev,
|
||||
[projectId]: value,
|
||||
}));
|
||||
},
|
||||
[projectId, setCustomSceneNamesByProject],
|
||||
);
|
||||
|
||||
return { hasCustomNames, setHasCustomNames, syncCustomSceneNames };
|
||||
}
|
||||
|
||||
export default useCustomSceneNames;
|
||||
14
src/hooks/useDevice.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { useContext } from 'preact/hooks';
|
||||
import DeviceContext from '../components/DeviceContext';
|
||||
|
||||
function useDevice() {
|
||||
const context = useContext(DeviceContext);
|
||||
|
||||
if (context === undefined) {
|
||||
throw new Error('useDevice must be used within a DeviceProvider');
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
export default useDevice;
|
||||
123
src/hooks/useDroppedFile.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { useAtom } from 'jotai';
|
||||
import JSZip from 'jszip';
|
||||
import type { DropTargetMonitor } from 'react-dnd';
|
||||
import { useDrop } from 'react-dnd';
|
||||
import { NativeTypes } from 'react-dnd-html5-backend';
|
||||
import {
|
||||
backupProjectIdsAtom,
|
||||
backupSkuAtom,
|
||||
droppedBackupFileAtom,
|
||||
droppedProjectFileAtom,
|
||||
unzippedBackupAtom,
|
||||
} from '~/atoms/droppedProjectFile';
|
||||
import { projectIdAtom } from '~/atoms/project';
|
||||
import { store } from '~/lib/store';
|
||||
import { showToast } from '~/lib/toast';
|
||||
import useDevice from './useDevice';
|
||||
|
||||
export const DROPPED_FILE_ID = '10';
|
||||
|
||||
function useDroppedFile() {
|
||||
const { device } = useDevice();
|
||||
const [, setProjectId] = useAtom(projectIdAtom);
|
||||
const [, setDroppedProjectFile] = useAtom(droppedProjectFileAtom);
|
||||
const [, setDroppedBackupFile] = useAtom(droppedBackupFileAtom);
|
||||
const [, setBackupSku] = useAtom(backupSkuAtom);
|
||||
const [, setBackupProjectIds] = useAtom(backupProjectIdsAtom);
|
||||
|
||||
const [{ isOver }, dropRef] = useDrop(
|
||||
() => ({
|
||||
accept: [NativeTypes.FILE],
|
||||
drop: async (_item: unknown, monitor: DropTargetMonitor) => {
|
||||
const item = monitor.getItem() as { files?: File[] };
|
||||
const file = item?.files?.[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileNameLower = file.name.toLowerCase();
|
||||
if (
|
||||
!(
|
||||
fileNameLower.endsWith('.tar') ||
|
||||
fileNameLower.endsWith('.ppak') ||
|
||||
fileNameLower.endsWith('.pak')
|
||||
)
|
||||
) {
|
||||
showToast('Unsupported file. Use a .tar or .ppak. or .pak', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!device && fileNameLower.endsWith('.tar')) {
|
||||
showToast(
|
||||
'Connect a device to load .tar files, or use a .pak/.ppak backup file',
|
||||
'error',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (device && (fileNameLower.endsWith('.pak') || fileNameLower.endsWith('.ppak'))) {
|
||||
showToast('Disconnect the device to use a .pak/.ppak backup file', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const buf = await file.arrayBuffer();
|
||||
|
||||
if (fileNameLower.endsWith('.tar')) {
|
||||
setDroppedProjectFile({ name: file.name, data: new Uint8Array(buf) });
|
||||
setDroppedBackupFile(null);
|
||||
store.set(unzippedBackupAtom, null);
|
||||
setBackupSku(null);
|
||||
setBackupProjectIds([]);
|
||||
setProjectId(DROPPED_FILE_ID);
|
||||
showToast(`Added "${file.name}" to projects`, 'info');
|
||||
} else if (fileNameLower.endsWith('.pak') || fileNameLower.endsWith('.ppak')) {
|
||||
const backupData = new Uint8Array(buf);
|
||||
setDroppedBackupFile(backupData);
|
||||
setDroppedProjectFile(null);
|
||||
|
||||
const unzipped = await JSZip.loadAsync(backupData);
|
||||
store.set(unzippedBackupAtom, unzipped);
|
||||
|
||||
// Read backup metadata
|
||||
const metaFile = unzipped.file('meta.json');
|
||||
if (metaFile) {
|
||||
try {
|
||||
const metaText = await metaFile.async('text');
|
||||
const meta = JSON.parse(metaText);
|
||||
const sku = meta.device_sku || meta.base_sku || null;
|
||||
setBackupSku(sku);
|
||||
} catch {
|
||||
setBackupSku(null);
|
||||
}
|
||||
} else {
|
||||
setBackupSku(null);
|
||||
}
|
||||
|
||||
// Scan for available projects
|
||||
const projectIds: number[] = [];
|
||||
for (const zipFile of Object.values(unzipped.files)) {
|
||||
const match = zipFile.name.match(/\/projects\/P(\d{2})\.tar$/);
|
||||
if (match) {
|
||||
projectIds.push(Number(match[1]));
|
||||
}
|
||||
}
|
||||
setBackupProjectIds(projectIds.sort((a, b) => a - b));
|
||||
|
||||
showToast(`Loaded backup file "${file.name}"`, 'info');
|
||||
}
|
||||
} catch {
|
||||
showToast('Failed to read dropped file', 'error');
|
||||
}
|
||||
},
|
||||
collect: (monitor: DropTargetMonitor) => ({
|
||||
isOver: monitor.isOver(),
|
||||
}),
|
||||
}),
|
||||
[device, setDroppedProjectFile, setProjectId, setBackupSku, setBackupProjectIds],
|
||||
);
|
||||
|
||||
return { dropRef, isOver };
|
||||
}
|
||||
|
||||
export default useDroppedFile;
|
||||
195
src/hooks/useExportProject.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useState } from 'preact/hooks';
|
||||
import { projectIdAtom } from '../atoms/project';
|
||||
import { trackEvent } from '../lib/ga';
|
||||
import { AbortError } from '../lib/utils';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportFormat,
|
||||
ExportFormatId,
|
||||
ExportResult,
|
||||
ExportStatus,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../types/types';
|
||||
import useProject from './useProject';
|
||||
import { getCustomSceneNames } from './useSceneName';
|
||||
|
||||
export const EXPORT_FORMATS: ExportFormat[] = [
|
||||
{
|
||||
name: 'Ableton 11+',
|
||||
value: 'ableton',
|
||||
},
|
||||
{
|
||||
name: 'DAWproject',
|
||||
value: 'dawproject',
|
||||
},
|
||||
{
|
||||
name: 'MIDI',
|
||||
value: 'midi',
|
||||
},
|
||||
{
|
||||
name: 'REAPER',
|
||||
value: 'reaper',
|
||||
},
|
||||
];
|
||||
|
||||
async function getExporterFn(
|
||||
format: ExportFormatId,
|
||||
): Promise<
|
||||
(
|
||||
projectId: string,
|
||||
data: any,
|
||||
progressCallback: any,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
) => Promise<any>
|
||||
> {
|
||||
switch (format) {
|
||||
case 'ableton':
|
||||
return (await import('../lib/exporters/ableton')).default;
|
||||
case 'dawproject':
|
||||
return (await import('../lib/exporters/dawProject')).default;
|
||||
case 'midi':
|
||||
return (await import('../lib/exporters/midi')).default;
|
||||
case 'reaper':
|
||||
return (await import('../lib/exporters/reaper')).default;
|
||||
default:
|
||||
throw new Error(`Unknown export format: ${format}`);
|
||||
}
|
||||
}
|
||||
|
||||
function filterScenes(data: ProjectRawData, params: ExporterParams): ProjectRawData {
|
||||
if (params.allScenes) {
|
||||
return data;
|
||||
}
|
||||
return {
|
||||
...data,
|
||||
scenes: data.scenes.filter((scene) => params.selectedScenes?.includes(scene.name)),
|
||||
};
|
||||
}
|
||||
|
||||
function useExportProject() {
|
||||
const projectId = useAtomValue(projectIdAtom);
|
||||
const { data: projectRawData } = useProject(projectId);
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
const [pendingStatus, setPendingStatus] = useState('');
|
||||
const [percentage, setPercentage] = useState(0);
|
||||
const [error, setError] = useState<any>(null);
|
||||
const [result, setResult] = useState<ExportResult | null>(null);
|
||||
const [sampleReport, setSampleReport] = useState<SampleReport | null>(null);
|
||||
const [abortController, setAbortController] = useState<AbortController | null>(null);
|
||||
|
||||
const startExport = async (params: ExporterParams & { format: ExportFormatId }) => {
|
||||
const { format, ...exporterParams } = params;
|
||||
|
||||
trackEvent('export_start', {
|
||||
format,
|
||||
});
|
||||
|
||||
try {
|
||||
const formatData = EXPORT_FORMATS.find((f) => f.value === format);
|
||||
|
||||
if (!formatData || !projectRawData) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPendingStatus('Starting export...');
|
||||
setError(null);
|
||||
setIsPending(true);
|
||||
setPercentage(1);
|
||||
|
||||
const controller = new AbortController();
|
||||
setAbortController(controller);
|
||||
|
||||
const exportFn = await getExporterFn(format);
|
||||
|
||||
const filteredData = filterScenes(projectRawData, exporterParams);
|
||||
|
||||
const customSceneNames = getCustomSceneNames(
|
||||
projectId,
|
||||
filteredData.scenes.map((s) => s.name),
|
||||
);
|
||||
|
||||
const result = await exportFn(
|
||||
projectId,
|
||||
filteredData,
|
||||
(stat: ExportStatus) => {
|
||||
setPercentage(stat.progress);
|
||||
setPendingStatus(stat.status);
|
||||
},
|
||||
{ ...exporterParams, customSceneNames },
|
||||
controller.signal,
|
||||
);
|
||||
|
||||
setResult(result);
|
||||
setSampleReport(result.sampleReport || null);
|
||||
setIsPending(false);
|
||||
setAbortController(null);
|
||||
|
||||
trackEvent('export_end');
|
||||
} catch (err) {
|
||||
if (err instanceof AbortError) {
|
||||
setPendingStatus('Export cancelled');
|
||||
setIsPending(false);
|
||||
setAbortController(null);
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
|
||||
Sentry.captureException(err);
|
||||
|
||||
setError(err);
|
||||
setIsPending(false);
|
||||
setAbortController(null);
|
||||
|
||||
trackEvent('export_error');
|
||||
}
|
||||
};
|
||||
|
||||
const cancelExport = () => {
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
setAbortController(null);
|
||||
}
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
setError(null);
|
||||
setIsPending(false);
|
||||
setPercentage(0);
|
||||
setPendingStatus('');
|
||||
setSampleReport(null);
|
||||
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
setAbortController(null);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of result.files) {
|
||||
URL.revokeObjectURL(file.url);
|
||||
}
|
||||
|
||||
setResult(null);
|
||||
};
|
||||
|
||||
return {
|
||||
startExport,
|
||||
cancelExport,
|
||||
reset,
|
||||
isPending,
|
||||
pendingStatus,
|
||||
percentage,
|
||||
result,
|
||||
error,
|
||||
sampleReport,
|
||||
};
|
||||
}
|
||||
|
||||
export default useExportProject;
|
||||
54
src/hooks/usePersistedFormValues.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { useEffect } from 'preact/hooks';
|
||||
import { UseFormReturn, useWatch } from 'react-hook-form';
|
||||
import { ZodType } from 'zod';
|
||||
|
||||
const STORAGE_KEY = 'export_form';
|
||||
|
||||
function pick<T extends Record<string, any>>(obj: T, keys: (keyof T)[]): Partial<T> {
|
||||
const result: Partial<T> = {};
|
||||
for (const key of keys) {
|
||||
if (key in obj) {
|
||||
result[key] = obj[key];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function usePersistedFormValues<T extends Record<string, any>>(
|
||||
form: UseFormReturn<T>,
|
||||
persistedFields: (keyof T)[],
|
||||
schema?: ZodType<T>,
|
||||
) {
|
||||
useEffect(() => {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored);
|
||||
const picked = pick(parsed, persistedFields);
|
||||
if (schema) {
|
||||
const result = schema.safeParse({ ...form.getValues(), ...picked });
|
||||
if (result.success) {
|
||||
form.reset(result.data as T);
|
||||
} else {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
}
|
||||
} else {
|
||||
form.reset({ ...form.getValues(), ...picked } as T);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to load persisted form values:', e);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const values = useWatch({ control: form.control });
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const toSave = pick(values as T, persistedFields);
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
|
||||
} catch (e) {
|
||||
console.warn('Failed to persist form values:', e);
|
||||
}
|
||||
}, [values]);
|
||||
}
|
||||
27
src/hooks/usePersistedState.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useState } from 'preact/hooks';
|
||||
|
||||
function usePersistedState<T>(key: string, defaultValue: T): [T, (value: T) => void] {
|
||||
const [state, setState] = useState<T>(() => {
|
||||
try {
|
||||
const item = localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) : defaultValue;
|
||||
} catch (error) {
|
||||
console.warn(`Error reading localStorage key "${key}":`, error);
|
||||
return defaultValue;
|
||||
}
|
||||
});
|
||||
|
||||
const setPersistedState = (value: T) => {
|
||||
try {
|
||||
setState(value);
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch (error) {
|
||||
console.warn(`Error writing to localStorage key "${key}":`, error);
|
||||
setState(value);
|
||||
}
|
||||
};
|
||||
|
||||
return [state, setPersistedState];
|
||||
}
|
||||
|
||||
export default usePersistedState;
|
||||
111
src/hooks/useProject.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import JSZip from 'jszip';
|
||||
import { deviceSkuAtom } from '~/atoms/deviceSku';
|
||||
import {
|
||||
backupSkuAtom,
|
||||
droppedBackupFileAtom,
|
||||
droppedProjectFileAtom,
|
||||
unzippedBackupAtom,
|
||||
} from '~/atoms/droppedProjectFile';
|
||||
import { getProjectFile } from '../lib/midi';
|
||||
import {
|
||||
collectEffects,
|
||||
collectPads,
|
||||
collectScenesAndPatterns,
|
||||
collectScenesSettings,
|
||||
collectSettings,
|
||||
collectSounds,
|
||||
loadSoundsFromBackup,
|
||||
} from '../lib/parsers';
|
||||
import { store } from '../lib/store';
|
||||
import { showToast } from '../lib/toast';
|
||||
import { untar } from '../lib/untar';
|
||||
import { ProjectRawData } from '../types/types';
|
||||
import useDevice from './useDevice';
|
||||
import { DROPPED_FILE_ID } from './useDroppedFile';
|
||||
|
||||
function useProject(id?: number | string) {
|
||||
const { device } = useDevice();
|
||||
const deviceSku = useAtomValue(deviceSkuAtom);
|
||||
const droppedProjectFile = useAtomValue(droppedProjectFileAtom);
|
||||
const droppedBackupFile = useAtomValue(droppedBackupFileAtom);
|
||||
const backupSku = useAtomValue(backupSkuAtom);
|
||||
|
||||
const result = useQuery<ProjectRawData | null>({
|
||||
queryKey: ['project', id],
|
||||
queryFn: async () => {
|
||||
if (!id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!droppedBackupFile && !device) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let archiveData: Uint8Array | undefined;
|
||||
let unzippedBackup: JSZip | undefined;
|
||||
|
||||
try {
|
||||
if (droppedBackupFile) {
|
||||
unzippedBackup = store.get(unzippedBackupAtom) ?? undefined;
|
||||
if (!unzippedBackup) {
|
||||
unzippedBackup = await JSZip.loadAsync(droppedBackupFile);
|
||||
}
|
||||
const projectFile = unzippedBackup.file(`/projects/P${String(id).padStart(2, '0')}.tar`);
|
||||
if (!projectFile) {
|
||||
throw new Error('No project file in backup');
|
||||
}
|
||||
const projectFileData = await projectFile.async('uint8array');
|
||||
archiveData = projectFileData;
|
||||
} else if (id === DROPPED_FILE_ID && droppedProjectFile) {
|
||||
archiveData = droppedProjectFile.data;
|
||||
} else {
|
||||
const archive = await getProjectFile(Number(id));
|
||||
archiveData = archive.data;
|
||||
}
|
||||
|
||||
const files = await untar(archiveData);
|
||||
const sounds = unzippedBackup
|
||||
? await loadSoundsFromBackup(unzippedBackup, files)
|
||||
: await collectSounds(files);
|
||||
const settings = collectSettings(files);
|
||||
const pads = collectPads(files, sounds);
|
||||
const scenes = collectScenesAndPatterns(files, device?.sku || backupSku || deviceSku);
|
||||
const scenesSettings = collectScenesSettings(files);
|
||||
const effects = collectEffects(files);
|
||||
|
||||
// @ts-expect-error wrong typing?
|
||||
const projectFileBlob = new Blob([archiveData]);
|
||||
const projectFile = new File(
|
||||
[projectFileBlob],
|
||||
`project-${String(id).padStart(2, '0')}.tar`,
|
||||
{
|
||||
type: 'application/x-tar',
|
||||
lastModified: Date.now(),
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
pads,
|
||||
scenes,
|
||||
settings,
|
||||
sounds,
|
||||
effects,
|
||||
projectFile,
|
||||
scenesSettings,
|
||||
};
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Failed to load project';
|
||||
showToast(message, 'error');
|
||||
return null;
|
||||
}
|
||||
},
|
||||
retry: false,
|
||||
enabled: !!id && (!!device || !!droppedBackupFile),
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default useProject;
|
||||
42
src/hooks/useProjectsList.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import {
|
||||
backupProjectIdsAtom,
|
||||
droppedBackupFileAtom,
|
||||
droppedProjectFileAtom,
|
||||
} from '~/atoms/droppedProjectFile';
|
||||
import useDevice from './useDevice';
|
||||
import { DROPPED_FILE_ID } from './useDroppedFile';
|
||||
|
||||
export type ProjectOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
function useProjectsList(): ProjectOption[] {
|
||||
const { device } = useDevice();
|
||||
const droppedProjectFile = useAtomValue(droppedProjectFileAtom);
|
||||
const droppedBackupFile = useAtomValue(droppedBackupFileAtom);
|
||||
const backupProjectIds = useAtomValue(backupProjectIdsAtom);
|
||||
|
||||
const base: ProjectOption[] = [];
|
||||
|
||||
if (device) {
|
||||
for (let i = 1; i <= 9; i++) {
|
||||
base.push({ value: String(i), label: `Project ${i}` });
|
||||
}
|
||||
} else if (droppedBackupFile) {
|
||||
if (backupProjectIds.length > 0) {
|
||||
for (const id of backupProjectIds) {
|
||||
base.push({ value: String(id), label: `Project ${id}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (droppedProjectFile) {
|
||||
base.push({ value: DROPPED_FILE_ID, label: droppedProjectFile.name });
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
export default useProjectsList;
|
||||
122
src/hooks/useSceneName.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useCallback, useEffect } from 'preact/hooks';
|
||||
import useCustomSceneNames from '~/hooks/useCustomSceneNames';
|
||||
import usePersistedState from '~/hooks/usePersistedState.ts';
|
||||
|
||||
function getSceneNameKey(name: string) {
|
||||
return `_s${name}_scene_name`;
|
||||
}
|
||||
|
||||
export function getDefaultSceneName(name: string) {
|
||||
return `SCENE ${name}`;
|
||||
}
|
||||
|
||||
export function getCustomSceneNames(
|
||||
projectId: string,
|
||||
sceneNames: string[],
|
||||
): Record<string, string> {
|
||||
const projectPrefix = `p${projectId}`;
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
for (const sceneName of sceneNames) {
|
||||
const defaultName = getDefaultSceneName(sceneName);
|
||||
const key = projectPrefix + getSceneNameKey(sceneName);
|
||||
try {
|
||||
const storedValue = localStorage.getItem(key);
|
||||
if (storedValue) {
|
||||
const parsed = JSON.parse(storedValue);
|
||||
if (parsed) {
|
||||
result[sceneName] = parsed;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
result[sceneName] = defaultName;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function useSceneName(projectId: string, originalName: string = '') {
|
||||
const projectPrefix = `p${projectId}`;
|
||||
const key = projectPrefix + getSceneNameKey(originalName);
|
||||
const defaultName = getDefaultSceneName(originalName);
|
||||
const [sceneName, setSceneNameState] = usePersistedState<string>(key, defaultName);
|
||||
const { hasCustomNames, setHasCustomNames, syncCustomSceneNames } =
|
||||
useCustomSceneNames(projectId);
|
||||
|
||||
useEffect(() => {
|
||||
if (originalName === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasCustomNames) {
|
||||
setSceneNameState(defaultName);
|
||||
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch (error) {
|
||||
console.warn(`Error removing localStorage key "${key}":`, error);
|
||||
}
|
||||
}
|
||||
}, [defaultName, hasCustomNames, key, originalName, setSceneNameState]);
|
||||
|
||||
const setSceneName = useCallback(
|
||||
(value: string) => {
|
||||
const normalizedValue = value.trim() === '' ? defaultName : value.trim();
|
||||
setSceneNameState(normalizedValue);
|
||||
|
||||
if (normalizedValue === defaultName) {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch (error) {
|
||||
console.warn(`Error removing localStorage key "${key}":`, error);
|
||||
}
|
||||
}
|
||||
|
||||
syncCustomSceneNames();
|
||||
},
|
||||
[defaultName, key, setSceneNameState, syncCustomSceneNames],
|
||||
);
|
||||
|
||||
const resetSceneNames = useCallback(() => {
|
||||
try {
|
||||
const keysToRemove = Object.keys(localStorage).filter((key) => key.startsWith(projectPrefix));
|
||||
|
||||
keysToRemove.forEach((key) => {
|
||||
localStorage.removeItem(key);
|
||||
});
|
||||
setHasCustomNames(false);
|
||||
} catch (error) {
|
||||
console.warn(`Error removing localStorage keys for "${projectPrefix}":`, error);
|
||||
}
|
||||
}, [projectPrefix, setHasCustomNames]);
|
||||
|
||||
function getSceneName(name: string) {
|
||||
const defaultName = getDefaultSceneName(name);
|
||||
|
||||
try {
|
||||
const key = projectPrefix + getSceneNameKey(name);
|
||||
const storedValue = localStorage.getItem(key);
|
||||
|
||||
if (!storedValue) {
|
||||
return defaultName;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(storedValue);
|
||||
} catch {
|
||||
return storedValue;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Error getSceneName "${key}":`, error);
|
||||
return defaultName;
|
||||
}
|
||||
}
|
||||
|
||||
return { sceneName, setSceneName, getSceneName, resetSceneNames };
|
||||
}
|
||||
|
||||
export default useSceneName;
|
||||
53
src/hooks/useSubmitFeedback.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import ky from 'ky';
|
||||
import { projectIdAtom } from '../atoms/project';
|
||||
import useDevice from './useDevice';
|
||||
import useProject from './useProject';
|
||||
|
||||
interface SubmitFeedbackParams {
|
||||
description: string;
|
||||
email: string;
|
||||
files: File[] | null;
|
||||
attachProject: boolean;
|
||||
}
|
||||
|
||||
function useSubmitFeedback() {
|
||||
const projectId = useAtomValue(projectIdAtom);
|
||||
const { device } = useDevice();
|
||||
const { data } = useProject(projectId);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (params: SubmitFeedbackParams) => {
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('description', params.description);
|
||||
formData.append('metadata', JSON.stringify(device?.metadata || {}));
|
||||
|
||||
if (params.email) {
|
||||
formData.append('email', params.email);
|
||||
}
|
||||
|
||||
if (params.files) {
|
||||
for (let i = 0; i < params.files.length; i++) {
|
||||
formData.append('files', params.files[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.attachProject && data?.projectFile) {
|
||||
formData.append('files', data.projectFile);
|
||||
}
|
||||
|
||||
return ky
|
||||
.post(`${import.meta.env.VITE_API_URL}/support/submit`, {
|
||||
body: formData,
|
||||
timeout: 100_000,
|
||||
})
|
||||
.json();
|
||||
},
|
||||
retry: 10,
|
||||
retryDelay: 2000,
|
||||
});
|
||||
}
|
||||
|
||||
export default useSubmitFeedback;
|
||||
28
src/hooks/useTheme.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useMemo } from 'preact/hooks';
|
||||
import { useSearch } from 'wouter-preact';
|
||||
import { deviceSkuAtom } from '~/atoms/deviceSku';
|
||||
import { SKU_EP133, SKU_TO_NAME } from '~/lib/constants';
|
||||
|
||||
function useTheme(): { id: string; name: string } {
|
||||
const deviceSku = useAtomValue(deviceSkuAtom);
|
||||
const search = useSearch();
|
||||
|
||||
const theme = useMemo(() => {
|
||||
const urlParams = new URLSearchParams(search);
|
||||
const themeParam = urlParams.get('theme');
|
||||
|
||||
if (themeParam) {
|
||||
const themeEntry = Object.entries(SKU_TO_NAME).find(([, value]) => value.id === themeParam);
|
||||
if (themeEntry) {
|
||||
return themeEntry[1];
|
||||
}
|
||||
}
|
||||
|
||||
return SKU_TO_NAME[deviceSku] || SKU_TO_NAME[SKU_EP133];
|
||||
}, [deviceSku, search]);
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
export default useTheme;
|
||||
23
src/index.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { render } from 'preact';
|
||||
import App from './App';
|
||||
import { swUpdateAvailableAtom } from './atoms/swUpdate';
|
||||
import { store } from './lib/store';
|
||||
import './styles.css';
|
||||
import { registerSW } from 'virtual:pwa-register';
|
||||
|
||||
if (import.meta.env.VITE_SENTRY_DSN) {
|
||||
Sentry.init({
|
||||
dsn: import.meta.env.VITE_SENTRY_DSN,
|
||||
sendDefaultPii: true,
|
||||
});
|
||||
}
|
||||
|
||||
registerSW({
|
||||
immediate: true,
|
||||
onNeedRefresh() {
|
||||
store.set(swUpdateAvailableAtom, true);
|
||||
},
|
||||
});
|
||||
|
||||
render(<App />, document.getElementById('app') || document.body);
|
||||
83
src/lib/constants.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
export const SKU_EP133 = 'TE032AS001';
|
||||
export const SKU_EP1320 = 'TE032AS005';
|
||||
export const SKU_EP40 = 'TE032AS006';
|
||||
|
||||
export const SKU_TO_NAME: Record<string, { id: string; name: string }> = {
|
||||
[SKU_EP133]: { id: 'ep133', name: 'EP-133' },
|
||||
[SKU_EP1320]: { id: 'ep1320', name: 'EP-1320' },
|
||||
[SKU_EP40]: { id: 'ep40', name: 'EP-40' },
|
||||
};
|
||||
|
||||
export const GROUPS = [
|
||||
{
|
||||
name: 'A',
|
||||
id: 'a',
|
||||
},
|
||||
{
|
||||
name: 'B',
|
||||
id: 'b',
|
||||
},
|
||||
{
|
||||
name: 'C',
|
||||
id: 'c',
|
||||
},
|
||||
{
|
||||
name: 'D',
|
||||
id: 'd',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const PADS = ['9', '8', '7', '6', '5', '4', '3', '2', '1', '.', '0', 'E'];
|
||||
|
||||
export const PAD_DISPLAY_FROM_INDEX: Record<number, string> = {
|
||||
0: '7',
|
||||
1: '8',
|
||||
2: '9',
|
||||
3: '4',
|
||||
4: '5',
|
||||
5: '6',
|
||||
6: '1',
|
||||
7: '2',
|
||||
8: '3',
|
||||
9: '.',
|
||||
10: '0',
|
||||
11: 'E',
|
||||
};
|
||||
|
||||
export const EFFECTS = {
|
||||
0: 'None',
|
||||
1: 'Delay',
|
||||
2: 'Reverb',
|
||||
3: 'Distortion',
|
||||
4: 'Chorus',
|
||||
5: 'Filter',
|
||||
6: 'Compressor',
|
||||
};
|
||||
|
||||
export const EFFECTS_SHORT = {
|
||||
0: 'OFF',
|
||||
1: 'DLY',
|
||||
2: 'RVB',
|
||||
3: 'DST',
|
||||
4: 'CHO',
|
||||
5: 'FLT',
|
||||
6: 'CMP',
|
||||
};
|
||||
|
||||
export const SCALES_SHORT: Record<number, string> = {
|
||||
0: '2T',
|
||||
1: 'MAJ',
|
||||
2: 'MIN',
|
||||
3: 'DOR',
|
||||
4: 'PHR',
|
||||
5: 'LYD',
|
||||
6: 'MIX',
|
||||
7: 'LOC',
|
||||
8: 'MA.P',
|
||||
9: 'MI.P',
|
||||
10: 'BLU',
|
||||
11: 'H.MI',
|
||||
12: 'M.MI',
|
||||
};
|
||||
|
||||
export const NOTE_NAMES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
|
||||
687
src/lib/exporters/ableton/builders.ts
Normal file
@@ -0,0 +1,687 @@
|
||||
import { create } from 'xmlbuilder2';
|
||||
import {
|
||||
EffectType,
|
||||
ExporterParams,
|
||||
FaderParam,
|
||||
ProjectRawData,
|
||||
ProjectSettings,
|
||||
} from '../../../types/types';
|
||||
import { EFFECTS } from '../../constants';
|
||||
import abletonTransformer, { AblClip, AblScene, AblTrack } from '../../transformers/ableton';
|
||||
import { getQuarterNotesPerBar } from '../utils';
|
||||
import { ALSDrumBranch } from './types/drumBranch';
|
||||
import { ALSDrumRack } from './types/drumRack';
|
||||
import { ALSChorus } from './types/effectChorus';
|
||||
import { ALSCompressor } from './types/effectCompressor';
|
||||
import { ALSDelay } from './types/effectDelay';
|
||||
import { ALSDistortion } from './types/effectDistortion';
|
||||
import { ALSFilter } from './types/effectFilter';
|
||||
import { ALSReverb } from './types/effectReverb';
|
||||
import { ALSGroupTrack } from './types/groupTrack';
|
||||
import { ALSMidiClip, ALSMidiClipContent } from './types/midiClip';
|
||||
import { ALSMidiTrack } from './types/midiTrack';
|
||||
import { ALSProject } from './types/project';
|
||||
import { ALSReturnTrack } from './types/returnTrack';
|
||||
import { ALSScene, ALSSceneContent } from './types/scene';
|
||||
import { ALSSimpler } from './types/simpler';
|
||||
import { ALSTrackSendHolder } from './types/trackSendHolder';
|
||||
import {
|
||||
filterFreqFromNormalized,
|
||||
fixIds,
|
||||
getId,
|
||||
gzipString,
|
||||
koEnvRangeToSeconds,
|
||||
loadTemplate,
|
||||
TIME_SIGNATURES,
|
||||
toNativePath,
|
||||
} from './utils';
|
||||
|
||||
let _localId = -1;
|
||||
let _localGroupId = -1;
|
||||
let _localTrackColor = -1;
|
||||
let _localGroupTrackColor = -1;
|
||||
|
||||
const MIN_CLIP_LAUNCHER_SCENES = 8;
|
||||
|
||||
async function buildMidiClip(
|
||||
koClip: AblClip,
|
||||
clipIdx: number,
|
||||
color: number,
|
||||
clipForLauncher: boolean = false,
|
||||
): Promise<ALSMidiClipContent> {
|
||||
const midiClipTemplate = await loadTemplate<ALSMidiClip>('midiClip');
|
||||
const midiClip = structuredClone(midiClipTemplate.MidiClip);
|
||||
|
||||
const beats = getQuarterNotesPerBar(
|
||||
koClip.timeSignature.numerator,
|
||||
koClip.timeSignature.denominator,
|
||||
);
|
||||
const time = koClip.offset * beats;
|
||||
const start = time;
|
||||
const end = time + koClip.sceneBars * beats;
|
||||
|
||||
midiClip['@Id'] = clipIdx;
|
||||
midiClip['@Time'] = time;
|
||||
midiClip.CurrentStart['@Value'] = start;
|
||||
midiClip.CurrentEnd['@Value'] = end;
|
||||
midiClip.Loop.LoopOn['@Value'] = 'true';
|
||||
midiClip.Loop.LoopStart['@Value'] = 0;
|
||||
midiClip.Loop.LoopEnd['@Value'] = koClip.bars * beats;
|
||||
midiClip.Loop.HiddenLoopStart['@Value'] = 0;
|
||||
midiClip.Loop.HiddenLoopEnd['@Value'] = koClip.bars * beats;
|
||||
midiClip.Color['@Value'] = color;
|
||||
midiClip.Name['@Value'] = koClip.sceneName;
|
||||
midiClip.TimeSignature.TimeSignatures.RemoteableTimeSignature.Numerator['@Value'] =
|
||||
koClip.timeSignature.numerator;
|
||||
midiClip.TimeSignature.TimeSignatures.RemoteableTimeSignature.Denominator['@Value'] =
|
||||
koClip.timeSignature.denominator;
|
||||
|
||||
if (clipForLauncher) {
|
||||
midiClip['@Time'] = 0;
|
||||
midiClip.CurrentStart['@Value'] = 0;
|
||||
midiClip.CurrentEnd['@Value'] = koClip.bars * beats;
|
||||
}
|
||||
|
||||
// ableton group notes
|
||||
const grouppedNotes: Record<string, typeof koClip.notes> = koClip.notes.reduce(
|
||||
(acc, note) => {
|
||||
const group = note.note.toString();
|
||||
acc[group] = acc[group] || [];
|
||||
acc[group].push(note);
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, typeof koClip.notes>,
|
||||
);
|
||||
|
||||
midiClip.Notes.KeyTracks.KeyTrack = [];
|
||||
let _noteId = 0;
|
||||
|
||||
Object.entries(grouppedNotes).forEach(([noteValueStr, notes], groupIndex) => {
|
||||
const noteValue = parseInt(noteValueStr, 10);
|
||||
|
||||
midiClip.Notes.KeyTracks.KeyTrack.push({
|
||||
'@Id': groupIndex,
|
||||
Notes: {
|
||||
MidiNoteEvent: notes.map((noteData, noteIndex) => {
|
||||
let dur = noteData.duration / 96;
|
||||
const nextNote = notes[noteIndex + 1];
|
||||
|
||||
// making sure same notes are not overlapping
|
||||
if (nextNote && noteData.position / 96 + dur > nextNote.position / 96) {
|
||||
dur = nextNote.position / 96 - noteData.position / 96;
|
||||
}
|
||||
|
||||
let velocity = noteData.velocity;
|
||||
|
||||
if (koClip.faderParams[FaderParam.VEL] !== -1) {
|
||||
velocity = koClip.faderParams[FaderParam.VEL] * 127;
|
||||
}
|
||||
|
||||
return {
|
||||
'@Time': noteData.position / 96,
|
||||
'@Duration': dur,
|
||||
'@Velocity': velocity,
|
||||
'@OffVelocity': 64,
|
||||
'@NoteId': _noteId++,
|
||||
};
|
||||
}),
|
||||
},
|
||||
MidiKey: {
|
||||
'@Value': noteValue,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return midiClip;
|
||||
}
|
||||
|
||||
async function buildSimplerDevice(koTrack: AblTrack) {
|
||||
const simplerTemplate = await loadTemplate<ALSSimpler>('simpler');
|
||||
const device = structuredClone(simplerTemplate.OriginalSimpler);
|
||||
|
||||
device.LastPresetRef.Value = {};
|
||||
switch (koTrack.playMode) {
|
||||
case 'legato':
|
||||
case 'oneshot':
|
||||
device.Globals.PlaybackMode['@Value'] = 1;
|
||||
break;
|
||||
case 'key':
|
||||
device.Globals.PlaybackMode['@Value'] = 0;
|
||||
break;
|
||||
}
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleRef.FileRef.RelativePath[
|
||||
'@Value'
|
||||
] = toNativePath(`Samples/Imported/${koTrack.sampleName}`);
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.Name['@Value'] = koTrack.name;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.RootKey['@Value'] = koTrack.rootNote;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleStart['@Value'] = koTrack.trimLeft;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleEnd['@Value'] = koTrack.trimRight;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.TimeSignature.TimeSignatures.RemoteableTimeSignature.Numerator[
|
||||
'@Value'
|
||||
] = koTrack.timeSignature.numerator;
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.TimeSignature.TimeSignatures.RemoteableTimeSignature.Denominator[
|
||||
'@Value'
|
||||
] = koTrack.timeSignature.denominator;
|
||||
|
||||
// setting velocity scaling to 20% to match KO behavior
|
||||
device.VolumeAndPan.VolumeVelScale.Manual['@Value'] = 0.2;
|
||||
|
||||
device.VolumeAndPan.Envelope.ReleaseTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.release, koTrack.soundLength) * 1000;
|
||||
device.VolumeAndPan.Envelope.AttackTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.attack, koTrack.soundLength) * 1000;
|
||||
device.VolumeAndPan.Panorama.Manual['@Value'] = koTrack.pan;
|
||||
|
||||
// overriding attack time if ATK fader param is defined
|
||||
if (koTrack.faderParams[FaderParam.ATK] !== -1) {
|
||||
device.VolumeAndPan.Envelope.AttackTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.faderParams[FaderParam.ATK] * 255, koTrack.soundLength) * 1000;
|
||||
}
|
||||
|
||||
// overriding release time if REL fader param is defined
|
||||
if (koTrack.faderParams[FaderParam.REL] !== -1) {
|
||||
device.VolumeAndPan.Envelope.ReleaseTime.Manual['@Value'] =
|
||||
koEnvRangeToSeconds(koTrack.faderParams[FaderParam.REL] * 255, koTrack.soundLength) * 1000;
|
||||
}
|
||||
|
||||
let pitch = koTrack.pitch || 0;
|
||||
if (koTrack.samplePitch) {
|
||||
pitch += koTrack.samplePitch;
|
||||
}
|
||||
|
||||
// adding PITCH fader param if defined
|
||||
if (koTrack.faderParams[FaderParam.PTC] !== -1) {
|
||||
const normalized = (koTrack.faderParams[FaderParam.PTC] - 0.5) * 2; // from 0-1 to -1 to +1
|
||||
pitch += Math.round(normalized * 5);
|
||||
}
|
||||
|
||||
// adding TUNE fader param if defined
|
||||
if (koTrack.faderParams[FaderParam.TUNE] !== -1) {
|
||||
pitch += Math.round(koTrack.faderParams[FaderParam.TUNE] * 23.5 - 12); // the range is slightly less than +/-12 cause that's how KO does it
|
||||
}
|
||||
|
||||
device.Pitch.TransposeKey.Manual['@Value'] = pitch;
|
||||
|
||||
// adding vibrato from MOD fader param if defined
|
||||
if (koTrack.faderParams[FaderParam.MOD] !== -1) {
|
||||
device.Lfo.IsOn.Manual['@Value'] = 'true';
|
||||
// KO's MOD wheel on 100% is roughly equal to 5% LFO pitch modulation
|
||||
device.Pitch.PitchLfoAmount.Manual['@Value'] = koTrack.faderParams[FaderParam.MOD] / 20;
|
||||
device.Lfo.Slot.Value.SimplerLfo.RateType.Manual['@Value'] = 1; // tempo synced 1/16
|
||||
}
|
||||
|
||||
if (koTrack.faderParams[FaderParam.LPF] !== -1 && koTrack.faderParams[FaderParam.LPF] !== 1) {
|
||||
device.Filter.IsOn.Manual['@Value'] = 'true';
|
||||
device.Filter.Slot.Value.SimplerFilter.Type.Manual['@Value'] = 0; // Low Pass
|
||||
device.Filter.Slot.Value.SimplerFilter.Slope.Manual['@Value'] = 'true'; // 24db/oct
|
||||
device.Filter.Slot.Value.SimplerFilter.Freq.Manual['@Value'] = filterFreqFromNormalized(
|
||||
koTrack.faderParams[FaderParam.LPF] * 0.9, // lowering max freq to match KO behavior
|
||||
);
|
||||
}
|
||||
|
||||
if (koTrack.faderParams[FaderParam.HPF] !== -1 && koTrack.faderParams[FaderParam.HPF] !== 0) {
|
||||
device.Filter.IsOn.Manual['@Value'] = 'true';
|
||||
device.Filter.Slot.Value.SimplerFilter.Type.Manual['@Value'] = 1; // High Pass
|
||||
device.Filter.Slot.Value.SimplerFilter.Slope.Manual['@Value'] = 'false'; // 12db/oct
|
||||
device.Filter.Slot.Value.SimplerFilter.Freq.Manual['@Value'] = filterFreqFromNormalized(
|
||||
koTrack.faderParams[FaderParam.HPF] * 0.7, // lowering max freq to match KO behavior
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: add bandpass filter
|
||||
|
||||
if (koTrack.timeStretch === 'bars') {
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.IsWarped[
|
||||
'@Value'
|
||||
] = 'true';
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMode[
|
||||
'@Value'
|
||||
] = 0; // warp in 'Beats' mode
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMarkers.WarpMarker =
|
||||
[
|
||||
{
|
||||
'@Id': 0,
|
||||
'@SecTime': 0,
|
||||
'@BeatTime': 0,
|
||||
},
|
||||
{
|
||||
'@Id': 1,
|
||||
'@SecTime': koTrack.soundLength,
|
||||
'@BeatTime': koTrack.timeStretchBars * koTrack.timeSignature.numerator, // convert bars to beats
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (koTrack.timeStretch === 'bpm') {
|
||||
// calculating beat length using koTrack.timeStretchBpm
|
||||
const beatTime = koTrack.soundLength / (60 / koTrack.timeStretchBpm);
|
||||
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.IsWarped[
|
||||
'@Value'
|
||||
] = 'true';
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMode[
|
||||
'@Value'
|
||||
] = 0; // warp in 'Beats' mode
|
||||
device.Player.MultiSampleMap.SampleParts.MultiSamplePart.SampleWarpProperties.WarpMarkers.WarpMarker =
|
||||
[
|
||||
{
|
||||
'@Id': 0,
|
||||
'@SecTime': 0,
|
||||
'@BeatTime': 0,
|
||||
},
|
||||
{
|
||||
'@Id': 1,
|
||||
'@SecTime': koTrack.soundLength,
|
||||
'@BeatTime': beatTime,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (koTrack.timeStretch === 'rev') {
|
||||
device.Player.Reverse.Manual['@Value'] = 'true';
|
||||
}
|
||||
|
||||
return {
|
||||
OriginalSimpler: device,
|
||||
};
|
||||
}
|
||||
|
||||
async function buildDrumRackDevice(koTrack: AblTrack) {
|
||||
const [drumRackTemplate, drumBranchTemplate] = await Promise.all([
|
||||
loadTemplate<ALSDrumRack>('drumRack'),
|
||||
loadTemplate<ALSDrumBranch>('drumBranch'),
|
||||
]);
|
||||
|
||||
const device = structuredClone(drumRackTemplate.DrumGroupDevice);
|
||||
|
||||
device.Branches.DrumBranch = [];
|
||||
|
||||
for (const [idx, subtrack] of koTrack.tracks.entries()) {
|
||||
if (!subtrack.sampleName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const padNumber = parseInt(subtrack.padCode.slice(1), 10);
|
||||
// EP133 is 3x4 (top→bottom), Ableton Drum Rack is 4x4 (bottom→top)
|
||||
// flip vertically: row 3 → row 0, row 0 → row 3
|
||||
const row = 3 - Math.floor(padNumber / 3);
|
||||
const col = padNumber % 3;
|
||||
const drumPad = row * 4 + col;
|
||||
|
||||
const drumBranch = structuredClone(drumBranchTemplate.DrumBranch);
|
||||
const note = 92 - drumPad; // 92 is the number of slot for C1 in the drum rack and it goes down
|
||||
|
||||
drumBranch['@Id'] = idx;
|
||||
drumBranch.Name.EffectiveName['@Value'] = subtrack.name;
|
||||
drumBranch.Name.UserName['@Value'] = subtrack.name;
|
||||
drumBranch.BranchInfo.ReceivingNote['@Value'] = note;
|
||||
drumBranch.BranchInfo.SendingNote['@Value'] = 60; // not sure if this matters
|
||||
drumBranch.BranchInfo.ChokeGroup['@Value'] = subtrack.inChokeGroup ? 1 : 0;
|
||||
drumBranch.DeviceChain.MidiToAudioDeviceChain.Devices = await buildSimplerDevice(subtrack);
|
||||
|
||||
device.Branches.DrumBranch.push(drumBranch);
|
||||
}
|
||||
|
||||
return {
|
||||
DrumGroupDevice: device,
|
||||
};
|
||||
}
|
||||
|
||||
async function buildTrack(
|
||||
koTrack: AblTrack,
|
||||
maxScenes: number,
|
||||
trackGroupId: number,
|
||||
exporterParams: ExporterParams,
|
||||
): Promise<ALSMidiTrack> {
|
||||
const midiTrackTemplate = await loadTemplate<ALSMidiTrack>('midiTrack');
|
||||
const midiTrack = structuredClone(midiTrackTemplate.MidiTrack);
|
||||
|
||||
midiTrack['@Id'] = _localId++;
|
||||
midiTrack.Name.EffectiveName['@Value'] = koTrack.soundId
|
||||
? `${String(koTrack.soundId).padStart(3, '0')} ${koTrack.name}`
|
||||
: koTrack.name;
|
||||
midiTrack.Name.UserName['@Value'] = midiTrack.Name.EffectiveName['@Value'];
|
||||
midiTrack.Color['@Value'] = 8 + _localTrackColor++; // started with 8th color in the Ableton palette, why not
|
||||
midiTrack.DeviceChain.MainSequencer.ClipTimeable.ArrangerAutomation.Events.MidiClip = [];
|
||||
midiTrack.TrackGroupId['@Value'] = trackGroupId;
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'] = [];
|
||||
midiTrack.DeviceChain.Mixer.Volume.Manual['@Value'] = koTrack.volume;
|
||||
|
||||
if (koTrack.faderParams[FaderParam.PAN] !== -1) {
|
||||
midiTrack.DeviceChain.Mixer.Pan.Manual['@Value'] =
|
||||
(koTrack.faderParams[FaderParam.PAN] - 0.5) * 2;
|
||||
}
|
||||
|
||||
if (koTrack.faderParams[FaderParam.LVL] !== -1) {
|
||||
// setting volume to the minimum of track volume and fader param
|
||||
midiTrack.DeviceChain.Mixer.Volume.Manual['@Value'] = Math.min(
|
||||
koTrack.volume,
|
||||
koTrack.faderParams[FaderParam.LVL],
|
||||
);
|
||||
}
|
||||
|
||||
if (koTrack.drumRack && exporterParams.includeArchivedSamples) {
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'].push(await buildDrumRackDevice(koTrack));
|
||||
}
|
||||
|
||||
if (!koTrack.drumRack && koTrack.sampleName && exporterParams.includeArchivedSamples) {
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'].push(await buildSimplerDevice(koTrack));
|
||||
}
|
||||
|
||||
// make sure id's are sequential in device chain
|
||||
midiTrack.DeviceChain.DeviceChain.Devices['#'].forEach((device, deviceIdx) => {
|
||||
Object.values(device).forEach((devContent: any) => {
|
||||
devContent['@Id'] = deviceIdx;
|
||||
});
|
||||
});
|
||||
|
||||
// make sure each tracks has the same empty slots for clips
|
||||
// must be equeal to GroupTrackSlot if this track is in a group
|
||||
// must be at least 8 slots to avoid Ableton crashes
|
||||
for (let sc = 0; sc < Math.max(MIN_CLIP_LAUNCHER_SCENES, maxScenes); sc++) {
|
||||
midiTrack.DeviceChain.MainSequencer.ClipSlotList.ClipSlot[sc] = {
|
||||
'@Id': sc,
|
||||
LomId: {
|
||||
'@Value': 0,
|
||||
},
|
||||
ClipSlot: {
|
||||
Value: {},
|
||||
},
|
||||
};
|
||||
midiTrack.DeviceChain.FreezeSequencer.ClipSlotList.ClipSlot[sc] = {
|
||||
'@Id': sc,
|
||||
LomId: {
|
||||
'@Value': 0,
|
||||
},
|
||||
ClipSlot: {
|
||||
Value: {},
|
||||
},
|
||||
HasStop: {
|
||||
'@Value': 'true',
|
||||
},
|
||||
NeedRefreeze: {
|
||||
'@Value': 'true',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (koTrack.lane) {
|
||||
if (!exporterParams.clips) {
|
||||
for (const [clipIdx, koClip] of koTrack.lane.clips.entries()) {
|
||||
const midiClip = await buildMidiClip(koClip, clipIdx, midiTrack.Color['@Value']);
|
||||
|
||||
midiTrack.DeviceChain.MainSequencer.ClipTimeable.ArrangerAutomation.Events.MidiClip.push(
|
||||
midiClip,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (const koClip of koTrack.lane.clips.values()) {
|
||||
const midiClip = await buildMidiClip(koClip, 0, midiTrack.Color['@Value'], true);
|
||||
|
||||
midiTrack.DeviceChain.MainSequencer.ClipSlotList.ClipSlot[koClip.sceneIndex] = {
|
||||
...midiTrack.DeviceChain.MainSequencer.ClipSlotList.ClipSlot[koClip.sceneIndex],
|
||||
ClipSlot: {
|
||||
Value: {
|
||||
MidiClip: midiClip,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exporterParams.groupTracks) {
|
||||
midiTrack.DeviceChain.AudioOutputRouting.Target['@Value'] = 'AudioOut/GroupTrack';
|
||||
midiTrack.DeviceChain.AudioOutputRouting.UpperDisplayString['@Value'] = 'Group';
|
||||
}
|
||||
|
||||
if (exporterParams.sendEffects && !exporterParams.groupTracks) {
|
||||
const trackSendHolderTemplate = await loadTemplate<ALSTrackSendHolder>('trackSendHolder');
|
||||
const trackSendHolder = structuredClone(trackSendHolderTemplate.TrackSendHolder);
|
||||
|
||||
trackSendHolder.Send.Manual['@Value'] = koTrack.faderParams[FaderParam.FX];
|
||||
|
||||
midiTrack.DeviceChain.Mixer.Sends.TrackSendHolder = trackSendHolder;
|
||||
}
|
||||
|
||||
return { MidiTrack: midiTrack };
|
||||
}
|
||||
|
||||
async function buildGroupTrack(
|
||||
koTrack: AblTrack,
|
||||
exporterParams: ExporterParams,
|
||||
maxScenes: number,
|
||||
) {
|
||||
const groupTrackTemplate = await loadTemplate<ALSGroupTrack>('groupTrack');
|
||||
const groupTrack = structuredClone(groupTrackTemplate.GroupTrack);
|
||||
|
||||
groupTrack['@Id'] = _localGroupId++;
|
||||
groupTrack.Name.EffectiveName['@Value'] = koTrack.group.toLocaleUpperCase();
|
||||
groupTrack.Name.UserName['@Value'] = koTrack.group.toLocaleUpperCase();
|
||||
groupTrack.Color['@Value'] = 5 + _localGroupTrackColor++;
|
||||
groupTrack.Slots.GroupTrackSlot = [];
|
||||
|
||||
// adding empty slots for clips (or Ableton will crash)
|
||||
// must be at least 8 slots to avoid Ableton crashes
|
||||
for (let sc = 0; sc < Math.max(MIN_CLIP_LAUNCHER_SCENES, maxScenes); sc++) {
|
||||
groupTrack.Slots.GroupTrackSlot.push({
|
||||
'@Id': sc,
|
||||
LomId: {
|
||||
'@Value': 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (exporterParams.sendEffects && koTrack.faderParams[FaderParam.FX] !== -1) {
|
||||
const trackSendHolderTemplate = await loadTemplate<ALSTrackSendHolder>('trackSendHolder');
|
||||
const trackSendHolder = structuredClone(trackSendHolderTemplate.TrackSendHolder);
|
||||
|
||||
trackSendHolder.Send.Manual['@Value'] = koTrack.faderParams[FaderParam.FX];
|
||||
|
||||
groupTrack.DeviceChain.Mixer.Sends.TrackSendHolder = trackSendHolder;
|
||||
}
|
||||
|
||||
return { GroupTrack: groupTrack };
|
||||
}
|
||||
|
||||
async function buildScenes(scenes: AblScene[], settings: ProjectSettings, maxScenes: number) {
|
||||
const sceneTemplate = await loadTemplate<ALSScene>('scene');
|
||||
const result: ALSSceneContent[] = [];
|
||||
const sceneCount = Math.max(MIN_CLIP_LAUNCHER_SCENES, maxScenes);
|
||||
|
||||
for (let index = 0; index < sceneCount; index++) {
|
||||
const scene = scenes[index];
|
||||
const sceneContent = structuredClone(sceneTemplate.Scene);
|
||||
|
||||
sceneContent['@Id'] = _localId++;
|
||||
sceneContent.Name['@Value'] = scene?.name || '';
|
||||
sceneContent.Tempo['@Value'] = settings.bpm;
|
||||
|
||||
result.push(sceneContent);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function buildTracks(tracks: AblTrack[], maxScenes: number, exporterParams: ExporterParams) {
|
||||
const result = [];
|
||||
let trackGroupId = -1;
|
||||
let currentGroup = '';
|
||||
|
||||
// if the tracks are grouped, we need to create a group track for each group BEFORE the children midi tracks
|
||||
// this took me around 3 hours to figure out
|
||||
for (const koTrack of tracks.sort((a, b) => a.group.localeCompare(b.group))) {
|
||||
if (exporterParams.groupTracks && koTrack.group !== currentGroup[0]) {
|
||||
currentGroup = koTrack.group;
|
||||
const groupTrack = await buildGroupTrack(koTrack, exporterParams, maxScenes);
|
||||
trackGroupId = groupTrack.GroupTrack['@Id'];
|
||||
result.push(groupTrack);
|
||||
}
|
||||
|
||||
const midiTrack = await buildTrack(koTrack, maxScenes, trackGroupId, exporterParams);
|
||||
|
||||
result.push(midiTrack);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function buildReturnTrack(projectData: ProjectRawData, trackId: number = 0) {
|
||||
const returnTrackTemplate = await loadTemplate<ALSReturnTrack>('returnTrack');
|
||||
const returnTrack = structuredClone(returnTrackTemplate.ReturnTrack);
|
||||
|
||||
returnTrack['@Id'] = trackId;
|
||||
returnTrack.Name.EffectiveName['@Value'] = EFFECTS[projectData.effects.effectType] || '';
|
||||
returnTrack.Name.UserName['@Value'] = EFFECTS[projectData.effects.effectType] || '';
|
||||
returnTrack.Color['@Value'] = 28;
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Reverb) {
|
||||
const effectReverbTemplate = await loadTemplate<ALSReverb>('effectReverb');
|
||||
const effectReverb = structuredClone(effectReverbTemplate.Reverb);
|
||||
|
||||
// these params are very rough approximation of the KO's reverb
|
||||
// it's not exact, but close enough
|
||||
effectReverb.PreDelay.Manual['@Value'] = 0.5;
|
||||
effectReverb.RoomSize.Manual['@Value'] = 300;
|
||||
effectReverb.ShelfHiFreq.Manual['@Value'] = 1000;
|
||||
effectReverb.DecayTime.Manual['@Value'] = projectData.effects.param1 * 8000; // max 8 seconds which is kinda close to how it sounds in KO
|
||||
effectReverb.ShelfHiGain.Manual['@Value'] = projectData.effects.param2; // param2 is "color" which roughly matches the high shelf gain cut amount
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Reverb: effectReverb };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Delay) {
|
||||
const effectDelayTemplate = await loadTemplate<ALSDelay>('effectDelay');
|
||||
const effectDelay = structuredClone(effectDelayTemplate.Delay);
|
||||
|
||||
effectDelay.DelayLine_SyncL.Manual['@Value'] = 'false';
|
||||
effectDelay.DelayLine_SyncR.Manual['@Value'] = 'false';
|
||||
// don't even ask how I came up with this formula
|
||||
effectDelay.DelayLine_TimeL.Manual['@Value'] =
|
||||
0.017397 * (287.725 ** projectData.effects.param1 - 1);
|
||||
effectDelay.DelayLine_TimeR.Manual['@Value'] =
|
||||
0.017397 * (287.725 ** projectData.effects.param1 - 1);
|
||||
effectDelay.Feedback.Manual['@Value'] = projectData.effects.param2;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Delay: effectDelay };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Distortion) {
|
||||
const effectDistortionTemplate = await loadTemplate<ALSDistortion>('effectDistortion');
|
||||
const effectDistortion = structuredClone(effectDistortionTemplate.Overdrive);
|
||||
|
||||
effectDistortion.Drive.Manual['@Value'] = projectData.effects.param1 * 50;
|
||||
effectDistortion.Tone.Manual['@Value'] = projectData.effects.param2 * 100;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Overdrive: effectDistortion };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Chorus) {
|
||||
const effectChorusTemplate = await loadTemplate<ALSChorus>('effectChorus');
|
||||
const effectChorus = structuredClone(effectChorusTemplate.Chorus2);
|
||||
|
||||
effectChorus.Amount.Manual['@Value'] = projectData.effects.param1;
|
||||
effectChorus.Feedback.Manual['@Value'] = projectData.effects.param2 * 0.8;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Chorus2: effectChorus };
|
||||
}
|
||||
|
||||
// putting a filter in return track is absolutely pointless, but hey, why not
|
||||
if (projectData.effects.effectType === EffectType.Filter) {
|
||||
const effectFilterTemplate = await loadTemplate<ALSFilter>('effectFilter');
|
||||
const effectFilter = structuredClone(effectFilterTemplate.AutoFilter);
|
||||
|
||||
effectFilter.Cutoff.Manual['@Value'] = 20 + projectData.effects.param1 * (135 - 20);
|
||||
effectFilter.Resonance.Manual['@Value'] = projectData.effects.param2 * 1.25;
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { AutoFilter: effectFilter };
|
||||
}
|
||||
|
||||
if (projectData.effects.effectType === EffectType.Compressor) {
|
||||
const effectCompressorTemplate = await loadTemplate<ALSCompressor>('effectCompressor');
|
||||
const effectCompressor = structuredClone(effectCompressorTemplate.Compressor2);
|
||||
|
||||
// params are ignored for now, just putting a basic compressor
|
||||
|
||||
returnTrack.DeviceChain.DeviceChain.Devices = { Compressor2: effectCompressor };
|
||||
}
|
||||
|
||||
return { ReturnTrack: returnTrack };
|
||||
}
|
||||
|
||||
export async function buildProject(projectData: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const transformedData = abletonTransformer(projectData, exporterParams);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('projectData', projectData);
|
||||
console.log('transformedData', transformedData);
|
||||
}
|
||||
|
||||
// reset local ids
|
||||
_localId = 1;
|
||||
_localGroupId = 50;
|
||||
_localTrackColor = 1;
|
||||
_localGroupTrackColor = 1;
|
||||
|
||||
const projectTemplate = await loadTemplate<ALSProject>('project');
|
||||
const project = structuredClone(projectTemplate);
|
||||
|
||||
// setting up tempo
|
||||
project.Ableton.LiveSet.MasterTrack.DeviceChain.Mixer.Tempo.Manual['@Value'] =
|
||||
projectData.settings.bpm;
|
||||
project.Ableton.LiveSet.MasterTrack.AutomationEnvelopes.Envelopes.AutomationEnvelope[1].Automation.Events.FloatEvent[
|
||||
'@Value'
|
||||
] = projectData.settings.bpm;
|
||||
// setting up time signature
|
||||
project.Ableton.LiveSet.MasterTrack.AutomationEnvelopes.Envelopes.AutomationEnvelope[0].Automation.Events.EnumEvent[
|
||||
'@Value'
|
||||
] =
|
||||
TIME_SIGNATURES[
|
||||
`${projectData.scenesSettings.timeSignature.numerator}/${projectData.scenesSettings.timeSignature.denominator}`
|
||||
] || TIME_SIGNATURES['4/4'];
|
||||
|
||||
project.Ableton.LiveSet.Tracks['#'] = await buildTracks(
|
||||
transformedData.tracks,
|
||||
transformedData.scenes.length,
|
||||
exporterParams,
|
||||
);
|
||||
|
||||
if (exporterParams.sendEffects) {
|
||||
const returnTrack = await buildReturnTrack(
|
||||
projectData,
|
||||
project.Ableton.LiveSet.Tracks['#'].length + 1,
|
||||
);
|
||||
|
||||
project.Ableton.LiveSet.Tracks['#'].push(returnTrack);
|
||||
}
|
||||
|
||||
if (exporterParams.clips) {
|
||||
project.Ableton.LiveSet.Scenes.Scene = await buildScenes(
|
||||
transformedData.scenes,
|
||||
projectData.settings,
|
||||
transformedData.scenes.length,
|
||||
);
|
||||
}
|
||||
|
||||
if (exporterParams.sendEffects) {
|
||||
project.Ableton.LiveSet.SendsPre = {
|
||||
SendPreBool: {
|
||||
'@Id': 1,
|
||||
'@Value': 'false',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const fixedRoot = fixIds(project);
|
||||
fixedRoot.Ableton.LiveSet.NextPointeeId['@Value'] = getId() + 1;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('ROOT', fixedRoot);
|
||||
}
|
||||
|
||||
const newXml = create(fixedRoot).end({ prettyPrint: true, indent: '\t' });
|
||||
const gzipped = gzipString(newXml);
|
||||
|
||||
return gzipped;
|
||||
}
|
||||
73
src/lib/exporters/ableton/index.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportResultFile,
|
||||
ExportStatus,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../../types/types';
|
||||
import { AbortError } from '../../utils';
|
||||
import { collectSamples } from '../utils';
|
||||
import { buildProject } from './builders';
|
||||
|
||||
async function exportAbleton(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ExportResult> {
|
||||
const files: ExportResultFile[] = [];
|
||||
const projectName = exporterParams.projectName || `Project${projectId}`;
|
||||
const zippedProject = new JSZip();
|
||||
|
||||
progressCallback({ progress: 1, status: 'Building project...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const alsFile = await buildProject(data, exporterParams);
|
||||
|
||||
zippedProject.file(`${projectName} Project/${projectName}.als`, alsFile);
|
||||
zippedProject.file(`${projectName} Project/Ableton Project Info/.dummy`, '');
|
||||
|
||||
let sampleReport: SampleReport | undefined;
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
samples.forEach((s) => {
|
||||
zippedProject.file(`${projectName} Project/Samples/Imported/${s.name}`, s.data);
|
||||
});
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle everything...' });
|
||||
|
||||
const zippedProjectFile = await zippedProject.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
files.push({
|
||||
name: `${projectName}.zip`,
|
||||
url: URL.createObjectURL(zippedProjectFile),
|
||||
type: 'archive',
|
||||
size: zippedProjectFile.size,
|
||||
});
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return {
|
||||
files,
|
||||
sampleReport,
|
||||
};
|
||||
}
|
||||
|
||||
export default exportAbleton;
|
||||
127
src/lib/exporters/ableton/templates/drumBranch.xml
Normal file
@@ -0,0 +1,127 @@
|
||||
<DrumBranch Id="0">
|
||||
<LomId Value="0" />
|
||||
<Name>
|
||||
<EffectiveName Value="005 nt kick d" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<IsSelected Value="false" />
|
||||
<DeviceChain>
|
||||
<MidiToAudioDeviceChain Id="0">
|
||||
<Devices>
|
||||
|
||||
</Devices>
|
||||
<SignalModulations />
|
||||
</MidiToAudioDeviceChain>
|
||||
</DeviceChain>
|
||||
<BranchSelectorRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0" />
|
||||
<CrossfadeMin Value="0" />
|
||||
<CrossfadeMax Value="0" />
|
||||
</BranchSelectorRange>
|
||||
<IsSoloed Value="false" />
|
||||
<SessionViewBranchWidth Value="55" />
|
||||
<IsHighlightedInSessionView Value="false" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Color Value="1" />
|
||||
<AutoColored Value="true" />
|
||||
<AutoColorScheme Value="0" />
|
||||
<SoloActivatedInSessionMixer Value="false" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<MixerDevice>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22191">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22192" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22193">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="1.99526238" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22194">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22195">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<Panorama>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22196">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22197">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Panorama>
|
||||
<SendInfos />
|
||||
<RoutingHelper>
|
||||
<Routable>
|
||||
<Target Value="AudioOut/None" />
|
||||
<UpperDisplayString Value="No Output" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</Routable>
|
||||
<TargetEnum Value="0" />
|
||||
</RoutingHelper>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</MixerDevice>
|
||||
<BranchInfo>
|
||||
<ReceivingNote Value="92" />
|
||||
<SendingNote Value="60" />
|
||||
<ChokeGroup Value="0" />
|
||||
</BranchInfo>
|
||||
</DrumBranch>
|
||||
460
src/lib/exporters/ableton/templates/drumRack.xml
Normal file
@@ -0,0 +1,460 @@
|
||||
<DrumGroupDevice Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22155">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22156" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Racks/Drum Racks/Drum Rack" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="DrumGroupDevice" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Racks/Drum Racks/Drum Rack" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:Synths#Drum%20Rack" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Racks/Drum Racks/Drum Rack" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="DrumGroupDevice" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:instr:DrumGroupDevice" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Branches>
|
||||
</Branches>
|
||||
<IsBranchesListVisible Value="false" />
|
||||
<IsReturnBranchesListVisible Value="false" />
|
||||
<IsRangesEditorVisible Value="false" />
|
||||
<AreDevicesVisible Value="false" />
|
||||
<NumVisibleMacroControls Value="8" />
|
||||
<MacroControls.0>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22157">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22158">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.0>
|
||||
<MacroControls.1>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22159">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22160">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.1>
|
||||
<MacroControls.2>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22161">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22162">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.2>
|
||||
<MacroControls.3>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22163">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22164">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.3>
|
||||
<MacroControls.4>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22165">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22166">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.4>
|
||||
<MacroControls.5>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22167">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22168">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.5>
|
||||
<MacroControls.6>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22169">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22170">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.6>
|
||||
<MacroControls.7>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22171">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22172">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.7>
|
||||
<MacroControls.8>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22173">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22174">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.8>
|
||||
<MacroControls.9>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22175">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22176">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.9>
|
||||
<MacroControls.10>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22177">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22178">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.10>
|
||||
<MacroControls.11>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22179">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22180">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.11>
|
||||
<MacroControls.12>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22181">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22182">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.12>
|
||||
<MacroControls.13>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22183">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22184">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.13>
|
||||
<MacroControls.14>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22185">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22186">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.14>
|
||||
<MacroControls.15>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22187">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22188">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MacroControls.15>
|
||||
<MacroDisplayNames.0 Value="Macro 1" />
|
||||
<MacroDisplayNames.1 Value="Macro 2" />
|
||||
<MacroDisplayNames.2 Value="Macro 3" />
|
||||
<MacroDisplayNames.3 Value="Macro 4" />
|
||||
<MacroDisplayNames.4 Value="Macro 5" />
|
||||
<MacroDisplayNames.5 Value="Macro 6" />
|
||||
<MacroDisplayNames.6 Value="Macro 7" />
|
||||
<MacroDisplayNames.7 Value="Macro 8" />
|
||||
<MacroDisplayNames.8 Value="Macro 9" />
|
||||
<MacroDisplayNames.9 Value="Macro 10" />
|
||||
<MacroDisplayNames.10 Value="Macro 11" />
|
||||
<MacroDisplayNames.11 Value="Macro 12" />
|
||||
<MacroDisplayNames.12 Value="Macro 13" />
|
||||
<MacroDisplayNames.13 Value="Macro 14" />
|
||||
<MacroDisplayNames.14 Value="Macro 15" />
|
||||
<MacroDisplayNames.15 Value="Macro 16" />
|
||||
<MacroDefaults.0 Value="-1" />
|
||||
<MacroDefaults.1 Value="-1" />
|
||||
<MacroDefaults.2 Value="-1" />
|
||||
<MacroDefaults.3 Value="-1" />
|
||||
<MacroDefaults.4 Value="-1" />
|
||||
<MacroDefaults.5 Value="-1" />
|
||||
<MacroDefaults.6 Value="-1" />
|
||||
<MacroDefaults.7 Value="-1" />
|
||||
<MacroDefaults.8 Value="-1" />
|
||||
<MacroDefaults.9 Value="-1" />
|
||||
<MacroDefaults.10 Value="-1" />
|
||||
<MacroDefaults.11 Value="-1" />
|
||||
<MacroDefaults.12 Value="-1" />
|
||||
<MacroDefaults.13 Value="-1" />
|
||||
<MacroDefaults.14 Value="-1" />
|
||||
<MacroDefaults.15 Value="-1" />
|
||||
<MacroAnnotations.0 Value="" />
|
||||
<MacroAnnotations.1 Value="" />
|
||||
<MacroAnnotations.2 Value="" />
|
||||
<MacroAnnotations.3 Value="" />
|
||||
<MacroAnnotations.4 Value="" />
|
||||
<MacroAnnotations.5 Value="" />
|
||||
<MacroAnnotations.6 Value="" />
|
||||
<MacroAnnotations.7 Value="" />
|
||||
<MacroAnnotations.8 Value="" />
|
||||
<MacroAnnotations.9 Value="" />
|
||||
<MacroAnnotations.10 Value="" />
|
||||
<MacroAnnotations.11 Value="" />
|
||||
<MacroAnnotations.12 Value="" />
|
||||
<MacroAnnotations.13 Value="" />
|
||||
<MacroAnnotations.14 Value="" />
|
||||
<MacroAnnotations.15 Value="" />
|
||||
<ForceDisplayGenericValue.0 Value="false" />
|
||||
<ForceDisplayGenericValue.1 Value="false" />
|
||||
<ForceDisplayGenericValue.2 Value="false" />
|
||||
<ForceDisplayGenericValue.3 Value="false" />
|
||||
<ForceDisplayGenericValue.4 Value="false" />
|
||||
<ForceDisplayGenericValue.5 Value="false" />
|
||||
<ForceDisplayGenericValue.6 Value="false" />
|
||||
<ForceDisplayGenericValue.7 Value="false" />
|
||||
<ForceDisplayGenericValue.8 Value="false" />
|
||||
<ForceDisplayGenericValue.9 Value="false" />
|
||||
<ForceDisplayGenericValue.10 Value="false" />
|
||||
<ForceDisplayGenericValue.11 Value="false" />
|
||||
<ForceDisplayGenericValue.12 Value="false" />
|
||||
<ForceDisplayGenericValue.13 Value="false" />
|
||||
<ForceDisplayGenericValue.14 Value="false" />
|
||||
<ForceDisplayGenericValue.15 Value="false" />
|
||||
<AreMacroControlsVisible Value="false" />
|
||||
<IsAutoSelectEnabled Value="true" />
|
||||
<ChainSelector>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22189">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22190">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ChainSelector>
|
||||
<ChainSelectorRelativePosition Value="-1073741824" />
|
||||
<ViewsToRestoreWhenUnfolding Value="0" />
|
||||
<ReturnBranches />
|
||||
<BranchesSplitterProportion Value="0.5" />
|
||||
<ShowBranchesInSessionMixer Value="false" />
|
||||
<MacroColor.0 Value="-1" />
|
||||
<MacroColor.1 Value="-1" />
|
||||
<MacroColor.2 Value="-1" />
|
||||
<MacroColor.3 Value="-1" />
|
||||
<MacroColor.4 Value="-1" />
|
||||
<MacroColor.5 Value="-1" />
|
||||
<MacroColor.6 Value="-1" />
|
||||
<MacroColor.7 Value="-1" />
|
||||
<MacroColor.8 Value="-1" />
|
||||
<MacroColor.9 Value="-1" />
|
||||
<MacroColor.10 Value="-1" />
|
||||
<MacroColor.11 Value="-1" />
|
||||
<MacroColor.12 Value="-1" />
|
||||
<MacroColor.13 Value="-1" />
|
||||
<MacroColor.14 Value="-1" />
|
||||
<MacroColor.15 Value="-1" />
|
||||
<LockId Value="0" />
|
||||
<LockSeal Value="0" />
|
||||
<ChainsListWrapper LomId="0" />
|
||||
<ReturnChainsListWrapper LomId="0" />
|
||||
<MacroVariations>
|
||||
<MacroSnapshots />
|
||||
</MacroVariations>
|
||||
<ExcludeMacroFromRandomization.0 Value="false" />
|
||||
<ExcludeMacroFromRandomization.1 Value="false" />
|
||||
<ExcludeMacroFromRandomization.2 Value="false" />
|
||||
<ExcludeMacroFromRandomization.3 Value="false" />
|
||||
<ExcludeMacroFromRandomization.4 Value="false" />
|
||||
<ExcludeMacroFromRandomization.5 Value="false" />
|
||||
<ExcludeMacroFromRandomization.6 Value="false" />
|
||||
<ExcludeMacroFromRandomization.7 Value="false" />
|
||||
<ExcludeMacroFromRandomization.8 Value="false" />
|
||||
<ExcludeMacroFromRandomization.9 Value="false" />
|
||||
<ExcludeMacroFromRandomization.10 Value="false" />
|
||||
<ExcludeMacroFromRandomization.11 Value="false" />
|
||||
<ExcludeMacroFromRandomization.12 Value="false" />
|
||||
<ExcludeMacroFromRandomization.13 Value="false" />
|
||||
<ExcludeMacroFromRandomization.14 Value="false" />
|
||||
<ExcludeMacroFromRandomization.15 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.0 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.1 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.2 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.3 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.4 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.5 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.6 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.7 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.8 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.9 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.10 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.11 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.12 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.13 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.14 Value="false" />
|
||||
<ExcludeMacroFromSnapshots.15 Value="false" />
|
||||
<AreMacroVariationsControlsVisible Value="false" />
|
||||
<ChainSelectorFilterMidiCtrl Value="false" />
|
||||
<RangeTypeIndex Value="1" />
|
||||
<ShowsZonesInsteadOfNoteNames Value="true" />
|
||||
<IsMidiSectionVisible Value="false" />
|
||||
<AreSendsVisible Value="false" />
|
||||
<ArePadsVisible Value="true" />
|
||||
<PadScrollPosition Value="19" />
|
||||
<DrumPadsListWrapper LomId="0" />
|
||||
<VisibleDrumPadsListWrapper LomId="0" />
|
||||
</DrumGroupDevice>
|
||||
248
src/lib/exporters/ableton/templates/effectChorus.xml
Normal file
@@ -0,0 +1,248 @@
|
||||
<Chorus2 Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23870">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23871" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Chorus-Ensemble" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Chorus2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Chorus-Ensemble" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Pitch%20&%20Modulation:Chorus-Ensemble" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Chorus-Ensemble" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Chorus2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Chorus2" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Mode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23872">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Mode>
|
||||
<Shaping>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23873">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23874">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Shaping>
|
||||
<Rate>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.9000000358" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="15" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23875">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23876">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Rate>
|
||||
<Amount>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23877">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23878">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Amount>
|
||||
<Feedback>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0.9900000095" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23879">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23880">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Feedback>
|
||||
<InvertFeedback>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23881">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</InvertFeedback>
|
||||
<VibratoOffset>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="180" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23882">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23883">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</VibratoOffset>
|
||||
<HighpassEnabled>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23884">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</HighpassEnabled>
|
||||
<HighpassFrequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="50.0000076" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="20" />
|
||||
<Max Value="2000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23885">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23886">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</HighpassFrequency>
|
||||
<Width>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23887">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23888">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Width>
|
||||
<Warmth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23889">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23890">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Warmth>
|
||||
<OutputGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.9999999404" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23891">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23892">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</OutputGain>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23893">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23894">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
</Chorus2>
|
||||
386
src/lib/exporters/ableton/templates/effectCompressor.xml
Normal file
@@ -0,0 +1,386 @@
|
||||
<Compressor2 Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="false" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23943">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23944" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Compressor" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Compressor2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Compressor" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Dynamics:Compressor" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Compressor" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Compressor2" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Compressor2" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<Threshold>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="1.99526238" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23945">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23946">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Threshold>
|
||||
<Ratio>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="4" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="340282326356119256160033759537265639424" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23947">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23948">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Ratio>
|
||||
<ExpansionRatio>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1.14999998" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23949">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23950">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ExpansionRatio>
|
||||
<Attack>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.009999999776" />
|
||||
<Max Value="1000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23951">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23952">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Attack>
|
||||
<Release>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="30" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="3000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23953">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23954">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Release>
|
||||
<AutoReleaseControlOnOff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23955">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</AutoReleaseControlOnOff>
|
||||
<Gain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-36" />
|
||||
<Max Value="36" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23956">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23957">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Gain>
|
||||
<GainCompensation>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23958">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</GainCompensation>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23959">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23960">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
<Model>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23961">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Model>
|
||||
<LegacyModel>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23962">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LegacyModel>
|
||||
<LogEnvelope>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23963">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</LogEnvelope>
|
||||
<LegacyEnvFollowerMode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23964">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LegacyEnvFollowerMode>
|
||||
<Knee>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="6" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="18" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23965">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23966">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Knee>
|
||||
<LookAhead>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23967">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LookAhead>
|
||||
<SideListen>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23968">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</SideListen>
|
||||
<SideChain>
|
||||
<OnOff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23969">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</OnOff>
|
||||
<RoutedInput>
|
||||
<Routable>
|
||||
<Target Value="AudioIn/None" />
|
||||
<UpperDisplayString Value="No Output" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</Routable>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="15.8489332" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23970">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23971">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
</RoutedInput>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23972">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23973">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
</SideChain>
|
||||
<SideChainEq>
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23974">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<Mode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="5" />
|
||||
<AutomationTarget Id="23975">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Mode>
|
||||
<Freq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="80" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="30" />
|
||||
<Max Value="15000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23976">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23977">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Freq>
|
||||
<Q>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.7071067691" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="12" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23978">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23979">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Q>
|
||||
<Gain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-15" />
|
||||
<Max Value="15" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23980">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23981">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Gain>
|
||||
</SideChainEq>
|
||||
<Live8LegacyMode Value="false" />
|
||||
<ViewMode Value="2" />
|
||||
<IsOutputCurveVisible Value="false" />
|
||||
<RmsTimeShort Value="8" />
|
||||
<RmsTimeLong Value="250" />
|
||||
<ReleaseTimeShort Value="15" />
|
||||
<ReleaseTimeLong Value="1500" />
|
||||
<CrossfaderSmoothingTime Value="10" />
|
||||
</Compressor2>
|
||||
379
src/lib/exporters/ableton/templates/effectDelay.xml
Normal file
@@ -0,0 +1,379 @@
|
||||
<Delay Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23801">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23802" />
|
||||
<LastSelectedTimeableIndex Value="4" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="2">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Delay" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Delay" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Delay" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Delay%20&%20Loop:Delay" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Delay" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Delay" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Delay" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<DelayLine_SmoothingMode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23803">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</DelayLine_SmoothingMode>
|
||||
<DelayLine_Link>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23804">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_Link>
|
||||
<DelayLine_PingPong>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23805">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_PingPong>
|
||||
<DelayLine_SyncL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23806">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_SyncL>
|
||||
<DelayLine_SyncR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23807">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</DelayLine_SyncR>
|
||||
<DelayLine_TimeL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.3749999404" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.001000000047" />
|
||||
<Max Value="5" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23808">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23809">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_TimeL>
|
||||
<DelayLine_TimeR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.3749999404" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.001000000047" />
|
||||
<Max Value="5" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23810">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23811">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_TimeR>
|
||||
<DelayLine_SimpleDelayTimeL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="300" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23812">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23813">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_SimpleDelayTimeL>
|
||||
<DelayLine_SimpleDelayTimeR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="300" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23814">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23815">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_SimpleDelayTimeR>
|
||||
<DelayLine_PingPongDelayTimeL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="999" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23816">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23817">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_PingPongDelayTimeL>
|
||||
<DelayLine_PingPongDelayTimeR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="1" />
|
||||
<Max Value="999" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23818">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23819">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_PingPongDelayTimeR>
|
||||
<DelayLine_SyncedSixteenthL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2" />
|
||||
<AutomationTarget Id="23820">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</DelayLine_SyncedSixteenthL>
|
||||
<DelayLine_SyncedSixteenthR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="3" />
|
||||
<AutomationTarget Id="23821">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</DelayLine_SyncedSixteenthR>
|
||||
<DelayLine_OffsetL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-0.3330000043" />
|
||||
<Max Value="0.3330000043" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23822">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23823">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_OffsetL>
|
||||
<DelayLine_OffsetR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-0.3330000043" />
|
||||
<Max Value="0.3330000043" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23824">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23825">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DelayLine_OffsetR>
|
||||
<DelayLine_CompatibilityMode Value="0" />
|
||||
<Feedback>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0.9499999881" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23826">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23827">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Feedback>
|
||||
<Freeze>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23828">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Freeze>
|
||||
<Filter_On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23829">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Filter_On>
|
||||
<Filter_Frequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="999.999878" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="49.9999962" />
|
||||
<Max Value="18000.0059" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23830">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23831">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Filter_Frequency>
|
||||
<Filter_Bandwidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="8" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="9" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23832">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23833">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Filter_Bandwidth>
|
||||
<Modulation_Frequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.01000000071" />
|
||||
<Max Value="39.9999962" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23834">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23835">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Modulation_Frequency>
|
||||
<Modulation_AmountTime>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23836">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23837">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Modulation_AmountTime>
|
||||
<Modulation_AmountFilter>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23838">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23839">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Modulation_AmountFilter>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23840">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23841">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
<DryWetMode Value="1" />
|
||||
<EcoProcessing Value="false" />
|
||||
</Delay>
|
||||
163
src/lib/exporters/ableton/templates/effectDistortion.xml
Normal file
@@ -0,0 +1,163 @@
|
||||
<Overdrive Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23856">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23857" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Overdrive" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Overdrive" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Overdrive" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Drive%20&%20Color:Overdrive" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Overdrive" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Overdrive" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Overdrive" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<MidFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1250" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="50" />
|
||||
<Max Value="20000" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23858">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23859">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MidFreq>
|
||||
<BandWidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="6.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="9" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23860">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23861">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BandWidth>
|
||||
<Drive>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="50" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="100" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23862">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23863">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Drive>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="100" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23864">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23865">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
<Tone>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="50" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="100" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23866">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23867">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Tone>
|
||||
<PreserveDynamics>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23868">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23869">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</PreserveDynamics>
|
||||
</Overdrive>
|
||||
435
src/lib/exporters/ableton/templates/effectFilter.xml
Normal file
@@ -0,0 +1,435 @@
|
||||
<AutoFilter Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="false" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23895">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23896" />
|
||||
<LastSelectedTimeableIndex Value="14" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="2">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Auto Filter" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="AutoFilter" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Auto Filter" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#EQ%20&%20Filters:Auto%20Filter" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Auto Filter" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="AutoFilter" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:AutoFilter" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<LegacyMode Value="false" />
|
||||
<LegacyFilterType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23897">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</LegacyFilterType>
|
||||
<FilterType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23898">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</FilterType>
|
||||
<CircuitLpHp>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23899">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CircuitLpHp>
|
||||
<CircuitBpNoMo>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23900">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CircuitBpNoMo>
|
||||
<Slope>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23901">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Slope>
|
||||
<Cutoff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="127" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="20" />
|
||||
<Max Value="135" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23902">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23903">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Cutoff>
|
||||
<CutoffLimit Value="135" />
|
||||
<LegacyQ>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.8199999928" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.200000003" />
|
||||
<Max Value="3" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23904">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23905">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</LegacyQ>
|
||||
<Resonance>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.1379310191" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1.25" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23906">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23907">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Resonance>
|
||||
<Morph>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23908">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23909">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Morph>
|
||||
<Drive>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="24" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23910">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23911">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Drive>
|
||||
<ModHub>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-127" />
|
||||
<Max Value="127" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23912">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23913">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ModHub>
|
||||
<Attack>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="6" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="30" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23914">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23915">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Attack>
|
||||
<Release>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="200" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.1000000015" />
|
||||
<Max Value="400" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23916">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23917">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Release>
|
||||
<LfoAmount>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="30" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23918">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23919">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</LfoAmount>
|
||||
<Lfo>
|
||||
<Type>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23920">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</Type>
|
||||
<Frequency>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.1000000089" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.009999999776" />
|
||||
<Max Value="10" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23921">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23922">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Frequency>
|
||||
<RateType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23923">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</RateType>
|
||||
<BeatRate>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="4" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="21" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23924">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23925">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BeatRate>
|
||||
<StereoMode>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23926">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</StereoMode>
|
||||
<Spin>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="0.5" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23927">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23928">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Spin>
|
||||
<Phase>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="360" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23929">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23930">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Phase>
|
||||
<Offset>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="360" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23931">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23932">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Offset>
|
||||
<IsOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23933">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</IsOn>
|
||||
<Quantize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23934">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Quantize>
|
||||
<BeatQuantize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2" />
|
||||
<AutomationTarget Id="23935">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</BeatQuantize>
|
||||
<NoiseWidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23936">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23937">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</NoiseWidth>
|
||||
</Lfo>
|
||||
<SideChain>
|
||||
<OnOff>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23938">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</OnOff>
|
||||
<RoutedInput>
|
||||
<Routable>
|
||||
<Target Value="AudioIn/None" />
|
||||
<UpperDisplayString Value="No Output" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</Routable>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="15.8489332" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23939">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23940">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
</RoutedInput>
|
||||
<DryWet>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23941">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23942">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DryWet>
|
||||
</SideChain>
|
||||
</AutoFilter>
|
||||
480
src/lib/exporters/ableton/templates/effectReverb.xml
Normal file
@@ -0,0 +1,480 @@
|
||||
<Reverb Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23747">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23748" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value>
|
||||
<AbletonDefaultPresetRef Id="2">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Reverb" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Reverb" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</Value>
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value>
|
||||
<BranchSourceContext Id="0">
|
||||
<OriginalFileRef>
|
||||
<FileRef Id="0">
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Reverb" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
</OriginalFileRef>
|
||||
<BrowserContentPath Value="query:AudioFx#Reverb%20&%20Resonance:Reverb" />
|
||||
<PresetRef>
|
||||
<AbletonDefaultPresetRef Id="0">
|
||||
<FileRef>
|
||||
<RelativePathType Value="5" />
|
||||
<RelativePath Value="Devices/Audio Effects/Reverb" />
|
||||
<Path Value="" />
|
||||
<Type Value="1" />
|
||||
<LivePackName Value="Core Library" />
|
||||
<LivePackId Value="www.ableton.com/0" />
|
||||
<OriginalFileSize Value="0" />
|
||||
<OriginalCrc Value="0" />
|
||||
</FileRef>
|
||||
<DeviceId Name="Reverb" />
|
||||
</AbletonDefaultPresetRef>
|
||||
</PresetRef>
|
||||
<BranchDeviceId Value="device:ableton:audiofx:Reverb" />
|
||||
</BranchSourceContext>
|
||||
</Value>
|
||||
</SourceContext>
|
||||
<OverwriteProtectionNumber Value="2819" />
|
||||
<PreDelay>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2.49999976" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="249.999969" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23749">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23750">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</PreDelay>
|
||||
<BandHighOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23751">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</BandHighOn>
|
||||
<BandLowOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23752">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</BandLowOn>
|
||||
<BandFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="829.999756" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="49.9999962" />
|
||||
<Max Value="18000.0059" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23753">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23754">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BandFreq>
|
||||
<BandWidth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="5.8499999" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.5" />
|
||||
<Max Value="9" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23755">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23756">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</BandWidth>
|
||||
<SpinOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23757">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</SpinOn>
|
||||
<EarlyReflectModFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.2977000177" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.07400000095" />
|
||||
<Max Value="1.29999983" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23758">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23759">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</EarlyReflectModFreq>
|
||||
<EarlyReflectModDepth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="17.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="2" />
|
||||
<Max Value="55" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23760">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23761">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</EarlyReflectModDepth>
|
||||
<DiffuseDelay>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.5" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23762">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23763">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DiffuseDelay>
|
||||
<ShelfHighOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23764">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</ShelfHighOn>
|
||||
<HighFilterType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<AutomationTarget Id="23765">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</HighFilterType>
|
||||
<ShelfHiFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="4500.00146" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="19.9999981" />
|
||||
<Max Value="15999.998" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23766">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23767">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfHiFreq>
|
||||
<ShelfHiGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.6999999881" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23768">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23769">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfHiGain>
|
||||
<ShelfLowOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23770">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</ShelfLowOn>
|
||||
<ShelfLoFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="90.0000076" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="19.9999981" />
|
||||
<Max Value="15000.001" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23771">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23772">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfLoFreq>
|
||||
<ShelfLoGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.75" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.200000003" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23773">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23774">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</ShelfLoGain>
|
||||
<ChorusOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23775">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</ChorusOn>
|
||||
<SizeModFreq>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.02000000142" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.01000000071" />
|
||||
<Max Value="8" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23776">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23777">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SizeModFreq>
|
||||
<SizeModDepth>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.01999999955" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.009999999776" />
|
||||
<Max Value="4" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23778">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23779">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SizeModDepth>
|
||||
<DecayTime>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1200.00012" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="199.999985" />
|
||||
<Max Value="60000.0039" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23780">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23781">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</DecayTime>
|
||||
<AllPassGain>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.6000000238" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.001000000047" />
|
||||
<Max Value="0.9599999785" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23782">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23783">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</AllPassGain>
|
||||
<AllPassSize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0.400000006" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.05000000075" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23784">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23785">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</AllPassSize>
|
||||
<FreezeOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="false" />
|
||||
<AutomationTarget Id="23786">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</FreezeOn>
|
||||
<FlatOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23787">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</FlatOn>
|
||||
<CutOn>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23788">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</CutOn>
|
||||
<RoomSize>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="99.9999924" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.2220000029" />
|
||||
<Max Value="499.999939" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23789">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23790">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</RoomSize>
|
||||
<SizeSmoothing>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="2" />
|
||||
<AutomationTarget Id="23791">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</SizeSmoothing>
|
||||
<StereoSeparation>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="100" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="120" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23792">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23793">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</StereoSeparation>
|
||||
<RoomType>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23794">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</RoomType>
|
||||
<MixReflect>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.02999999933" />
|
||||
<Max Value="1.99530005" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23795">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23796">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MixReflect>
|
||||
<MixDiffuse>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.02999999933" />
|
||||
<Max Value="1.99530005" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23797">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23798">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MixDiffuse>
|
||||
<MixDirect>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23799">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23800">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</MixDirect>
|
||||
<StereoSeparationOnDrySignal Value="false" />
|
||||
</Reverb>
|
||||
269
src/lib/exporters/ableton/templates/groupTrack.xml
Normal file
@@ -0,0 +1,269 @@
|
||||
<GroupTrack Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<PreferredContentViewMode Value="0" />
|
||||
<TrackDelay>
|
||||
<Value Value="0" />
|
||||
<IsValueSampleBased Value="false" />
|
||||
</TrackDelay>
|
||||
<Name>
|
||||
<EffectiveName Value="1-Group" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<Color Value="-1" />
|
||||
<AutomationEnvelopes>
|
||||
<Envelopes />
|
||||
</AutomationEnvelopes>
|
||||
<TrackGroupId Value="-1" />
|
||||
<TrackUnfolded Value="true" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
<ViewData Value="{}" />
|
||||
<TakeLanes>
|
||||
<TakeLanes />
|
||||
<AreTakeLanesFolded Value="true" />
|
||||
</TakeLanes>
|
||||
<LinkedTrackGroupId Value="-1" />
|
||||
<Slots>
|
||||
</Slots>
|
||||
<DeviceChain>
|
||||
<AutomationLanes>
|
||||
<AutomationLanes>
|
||||
<AutomationLane Id="0">
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<LaneHeight Value="51" />
|
||||
</AutomationLane>
|
||||
</AutomationLanes>
|
||||
<AreAdditionalAutomationLanesFolded Value="false" />
|
||||
</AutomationLanes>
|
||||
<ClipEnvelopeChooserViewState>
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<PreferModulationVisible Value="false" />
|
||||
</ClipEnvelopeChooserViewState>
|
||||
<AudioInputRouting>
|
||||
<Target Value="AudioIn/External/S0" />
|
||||
<UpperDisplayString Value="Ext. In" />
|
||||
<LowerDisplayString Value="1/2" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioInputRouting>
|
||||
<MidiInputRouting>
|
||||
<Target Value="MidiIn/External.All/-1" />
|
||||
<UpperDisplayString Value="Ext: All Ins" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiInputRouting>
|
||||
<AudioOutputRouting>
|
||||
<Target Value="AudioOut/Master" />
|
||||
<UpperDisplayString Value="Master" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioOutputRouting>
|
||||
<MidiOutputRouting>
|
||||
<Target Value="MidiOut/None" />
|
||||
<UpperDisplayString Value="None" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiOutputRouting>
|
||||
<Mixer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22311">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22312" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Sends />
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22317">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<SoloSink Value="false" />
|
||||
<PanMode Value="0" />
|
||||
<Pan>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22318">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22319">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Pan>
|
||||
<SplitStereoPanL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="-1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22320">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22321">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanL>
|
||||
<SplitStereoPanR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22322">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22323">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanR>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0.0003162277571" />
|
||||
<Max Value="1.99526238" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22316">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22317">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<ViewStateSesstionTrackWidth Value="93" />
|
||||
<CrossFadeState>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="22326">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CrossFadeState>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</Mixer>
|
||||
<DeviceChain>
|
||||
<Devices />
|
||||
<SignalModulations />
|
||||
</DeviceChain>
|
||||
<FreezeSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22327">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22328" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList />
|
||||
<MonitoringEnum Value="1" />
|
||||
<Sample>
|
||||
<ArrangerAutomation>
|
||||
<Events />
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</Sample>
|
||||
<VolumeModulationTarget Id="22329">
|
||||
<LockEnvelope Value="0" />
|
||||
</VolumeModulationTarget>
|
||||
<TranspositionModulationTarget Id="22330">
|
||||
<LockEnvelope Value="0" />
|
||||
</TranspositionModulationTarget>
|
||||
<GrainSizeModulationTarget Id="22331">
|
||||
<LockEnvelope Value="0" />
|
||||
</GrainSizeModulationTarget>
|
||||
<FluxModulationTarget Id="22332">
|
||||
<LockEnvelope Value="0" />
|
||||
</FluxModulationTarget>
|
||||
<SampleOffsetModulationTarget Id="22333">
|
||||
<LockEnvelope Value="0" />
|
||||
</SampleOffsetModulationTarget>
|
||||
<PitchViewScrollPosition Value="-1073741824" />
|
||||
<SampleOffsetModulationScrollPosition Value="-1073741824" />
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="1" />
|
||||
</Recorder>
|
||||
</FreezeSequencer>
|
||||
</DeviceChain>
|
||||
</GroupTrack>
|
||||
119
src/lib/exporters/ableton/templates/midiClip.xml
Normal file
@@ -0,0 +1,119 @@
|
||||
<MidiClip Id="0" Time="0">
|
||||
<LomId Value="0"/>
|
||||
<LomIdView Value="0"/>
|
||||
<CurrentStart Value="0"/>
|
||||
<CurrentEnd Value="4"/>
|
||||
<Loop>
|
||||
<LoopStart Value="0"/>
|
||||
<LoopEnd Value="4"/>
|
||||
<StartRelative Value="0"/>
|
||||
<LoopOn Value="true"/>
|
||||
<OutMarker Value="4"/>
|
||||
<HiddenLoopStart Value="0"/>
|
||||
<HiddenLoopEnd Value="4"/>
|
||||
</Loop>
|
||||
<Name Value=""/>
|
||||
<Annotation Value=""/>
|
||||
<Color Value="10"/>
|
||||
<LaunchMode Value="0"/>
|
||||
<LaunchQuantisation Value="0"/>
|
||||
<TimeSignature>
|
||||
<TimeSignatures>
|
||||
<RemoteableTimeSignature Id="0">
|
||||
<Numerator Value="4"/>
|
||||
<Denominator Value="4"/>
|
||||
<Time Value="0"/>
|
||||
</RemoteableTimeSignature>
|
||||
</TimeSignatures>
|
||||
</TimeSignature>
|
||||
<Envelopes>
|
||||
<Envelopes/>
|
||||
</Envelopes>
|
||||
<ScrollerTimePreserver>
|
||||
<LeftTime Value="0"/>
|
||||
<RightTime Value="4"/>
|
||||
</ScrollerTimePreserver>
|
||||
<TimeSelection>
|
||||
<AnchorTime Value="0"/>
|
||||
<OtherTime Value="0"/>
|
||||
</TimeSelection>
|
||||
<Legato Value="false"/>
|
||||
<Ram Value="false"/>
|
||||
<GrooveSettings>
|
||||
<GrooveId Value="-1"/>
|
||||
</GrooveSettings>
|
||||
<Disabled Value="false"/>
|
||||
<VelocityAmount Value="0"/>
|
||||
<FollowAction>
|
||||
<FollowTime Value="4"/>
|
||||
<IsLinked Value="true"/>
|
||||
<LoopIterations Value="1"/>
|
||||
<FollowActionA Value="4"/>
|
||||
<FollowActionB Value="0"/>
|
||||
<FollowChanceA Value="100"/>
|
||||
<FollowChanceB Value="0"/>
|
||||
<JumpIndexA Value="1"/>
|
||||
<JumpIndexB Value="1"/>
|
||||
<FollowActionEnabled Value="false"/>
|
||||
</FollowAction>
|
||||
<Grid>
|
||||
<FixedNumerator Value="1"/>
|
||||
<FixedDenominator Value="16"/>
|
||||
<GridIntervalPixel Value="20"/>
|
||||
<Ntoles Value="2"/>
|
||||
<SnapToGrid Value="true"/>
|
||||
<Fixed Value="true"/>
|
||||
</Grid>
|
||||
<FreezeStart Value="0"/>
|
||||
<FreezeEnd Value="0"/>
|
||||
<IsWarped Value="true"/>
|
||||
<TakeId Value="1"/>
|
||||
<IsInKey Value="true"/>
|
||||
<ScaleInformation>
|
||||
<Root Value="0"/>
|
||||
<Name Value="Major"/>
|
||||
</ScaleInformation>
|
||||
<Notes>
|
||||
<KeyTracks>
|
||||
<KeyTrack Id="0">
|
||||
<Notes>
|
||||
<MidiNoteEvent Time="0" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="1"/>
|
||||
<MidiNoteEvent Time="1" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="2"/>
|
||||
<MidiNoteEvent Time="2" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="3"/>
|
||||
<MidiNoteEvent Time="3" Duration="0.25" Velocity="100" OffVelocity="64" NoteId="4"/>
|
||||
</Notes>
|
||||
<MidiKey Value="60"/>
|
||||
</KeyTrack>
|
||||
</KeyTracks>
|
||||
<PerNoteEventStore>
|
||||
<EventLists/>
|
||||
</PerNoteEventStore>
|
||||
<NoteProbabilityGroups/>
|
||||
<ProbabilityGroupIdGenerator>
|
||||
<NextId Value="1"/>
|
||||
</ProbabilityGroupIdGenerator>
|
||||
<NoteIdGenerator>
|
||||
<NextId Value="5"/>
|
||||
</NoteIdGenerator>
|
||||
</Notes>
|
||||
<BankSelectCoarse Value="-1"/>
|
||||
<BankSelectFine Value="-1"/>
|
||||
<ProgramChange Value="-1"/>
|
||||
<NoteEditorFoldInZoom Value="-1"/>
|
||||
<NoteEditorFoldInScroll Value="0"/>
|
||||
<NoteEditorFoldOutZoom Value="867"/>
|
||||
<NoteEditorFoldOutScroll Value="-413"/>
|
||||
<NoteEditorFoldScaleZoom Value="-1"/>
|
||||
<NoteEditorFoldScaleScroll Value="0"/>
|
||||
<NoteSpellingPreference Value="0"/>
|
||||
<AccidentalSpellingPreference Value="3"/>
|
||||
<PreferFlatRootNote Value="false"/>
|
||||
<ExpressionGrid>
|
||||
<FixedNumerator Value="1"/>
|
||||
<FixedDenominator Value="16"/>
|
||||
<GridIntervalPixel Value="20"/>
|
||||
<Ntoles Value="2"/>
|
||||
<SnapToGrid Value="false"/>
|
||||
<Fixed Value="false"/>
|
||||
</ExpressionGrid>
|
||||
</MidiClip>
|
||||
850
src/lib/exporters/ableton/templates/midiTrack.xml
Normal file
@@ -0,0 +1,850 @@
|
||||
<MidiTrack Id="0">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<PreferredContentViewMode Value="0" />
|
||||
<TrackDelay>
|
||||
<Value Value="0" />
|
||||
<IsValueSampleBased Value="false" />
|
||||
</TrackDelay>
|
||||
<Name>
|
||||
<EffectiveName Value="TRACK_1" />
|
||||
<UserName Value="TRACK_1" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<Color Value="10" />
|
||||
<AutomationEnvelopes>
|
||||
<Envelopes />
|
||||
</AutomationEnvelopes>
|
||||
<TrackGroupId Value="-1" />
|
||||
<TrackUnfolded Value="true" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
<ViewData Value="{}" />
|
||||
<TakeLanes>
|
||||
<TakeLanes />
|
||||
<AreTakeLanesFolded Value="true" />
|
||||
</TakeLanes>
|
||||
<LinkedTrackGroupId Value="-1" />
|
||||
<SavedPlayingSlot Value="-1" />
|
||||
<SavedPlayingOffset Value="0" />
|
||||
<Freeze Value="false" />
|
||||
<NeedArrangerRefreeze Value="true" />
|
||||
<PostProcessFreezeClips Value="0" />
|
||||
<DeviceChain>
|
||||
<AutomationLanes>
|
||||
<AutomationLanes>
|
||||
<AutomationLane Id="0">
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<LaneHeight Value="68" />
|
||||
</AutomationLane>
|
||||
</AutomationLanes>
|
||||
<AreAdditionalAutomationLanesFolded Value="false" />
|
||||
</AutomationLanes>
|
||||
<ClipEnvelopeChooserViewState>
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<PreferModulationVisible Value="false" />
|
||||
</ClipEnvelopeChooserViewState>
|
||||
<AudioInputRouting>
|
||||
<Target Value="AudioIn/External/S0" />
|
||||
<UpperDisplayString Value="Ext. In" />
|
||||
<LowerDisplayString Value="1/2" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioInputRouting>
|
||||
<MidiInputRouting>
|
||||
<Target Value="MidiIn/External.All/-1" />
|
||||
<UpperDisplayString Value="Ext: All Ins" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiInputRouting>
|
||||
<AudioOutputRouting>
|
||||
<Target Value="AudioOut/Master" />
|
||||
<UpperDisplayString Value="Master" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioOutputRouting>
|
||||
<MidiOutputRouting>
|
||||
<Target Value="MidiOut/None" />
|
||||
<UpperDisplayString Value="None" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiOutputRouting>
|
||||
<Mixer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22182">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22183" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="false" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Sends />
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22184">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<SoloSink Value="false" />
|
||||
<PanMode Value="0" />
|
||||
<Pan>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22185">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22186">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Pan>
|
||||
<SplitStereoPanL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="-1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22187">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22188">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanL>
|
||||
<SplitStereoPanR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22189">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22190">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanR>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="22191">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="22192">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<ViewStateSessionTrackWidth Value="93" />
|
||||
<CrossFadeState>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="22193">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CrossFadeState>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</Mixer>
|
||||
<MainSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22194">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22195" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList>
|
||||
<ClipSlot Id="0">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="1">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="2">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="3">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="4">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="5">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="6">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="7">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
</ClipSlotList>
|
||||
<MonitoringEnum Value="1" />
|
||||
<ClipTimeable>
|
||||
<ArrangerAutomation>
|
||||
<Events>
|
||||
<MidiClip />
|
||||
<MidiClip />
|
||||
</Events>
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</ClipTimeable>
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="0" />
|
||||
</Recorder>
|
||||
<MidiControllers>
|
||||
<ControllerTargets.0 Id="22196">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.0>
|
||||
<ControllerTargets.1 Id="22197">
|
||||
<LockEnvelope Value="1" />
|
||||
</ControllerTargets.1>
|
||||
<ControllerTargets.2 Id="22198">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.2>
|
||||
<ControllerTargets.3 Id="22199">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.3>
|
||||
<ControllerTargets.4 Id="22200">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.4>
|
||||
<ControllerTargets.5 Id="22201">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.5>
|
||||
<ControllerTargets.6 Id="22202">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.6>
|
||||
<ControllerTargets.7 Id="22203">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.7>
|
||||
<ControllerTargets.8 Id="22204">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.8>
|
||||
<ControllerTargets.9 Id="22205">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.9>
|
||||
<ControllerTargets.10 Id="22206">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.10>
|
||||
<ControllerTargets.11 Id="22207">
|
||||
<LockEnvelope Value="1" />
|
||||
</ControllerTargets.11>
|
||||
<ControllerTargets.12 Id="22208">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.12>
|
||||
<ControllerTargets.13 Id="22209">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.13>
|
||||
<ControllerTargets.14 Id="22210">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.14>
|
||||
<ControllerTargets.15 Id="22211">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.15>
|
||||
<ControllerTargets.16 Id="22212">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.16>
|
||||
<ControllerTargets.17 Id="22213">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.17>
|
||||
<ControllerTargets.18 Id="22214">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.18>
|
||||
<ControllerTargets.19 Id="22215">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.19>
|
||||
<ControllerTargets.20 Id="22216">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.20>
|
||||
<ControllerTargets.21 Id="22217">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.21>
|
||||
<ControllerTargets.22 Id="22218">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.22>
|
||||
<ControllerTargets.23 Id="22219">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.23>
|
||||
<ControllerTargets.24 Id="22220">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.24>
|
||||
<ControllerTargets.25 Id="22221">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.25>
|
||||
<ControllerTargets.26 Id="22222">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.26>
|
||||
<ControllerTargets.27 Id="22223">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.27>
|
||||
<ControllerTargets.28 Id="22224">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.28>
|
||||
<ControllerTargets.29 Id="22225">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.29>
|
||||
<ControllerTargets.30 Id="22226">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.30>
|
||||
<ControllerTargets.31 Id="22227">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.31>
|
||||
<ControllerTargets.32 Id="22228">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.32>
|
||||
<ControllerTargets.33 Id="22229">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.33>
|
||||
<ControllerTargets.34 Id="22230">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.34>
|
||||
<ControllerTargets.35 Id="22231">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.35>
|
||||
<ControllerTargets.36 Id="22232">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.36>
|
||||
<ControllerTargets.37 Id="22233">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.37>
|
||||
<ControllerTargets.38 Id="22234">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.38>
|
||||
<ControllerTargets.39 Id="22235">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.39>
|
||||
<ControllerTargets.40 Id="22236">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.40>
|
||||
<ControllerTargets.41 Id="22237">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.41>
|
||||
<ControllerTargets.42 Id="22238">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.42>
|
||||
<ControllerTargets.43 Id="22239">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.43>
|
||||
<ControllerTargets.44 Id="22240">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.44>
|
||||
<ControllerTargets.45 Id="22241">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.45>
|
||||
<ControllerTargets.46 Id="22242">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.46>
|
||||
<ControllerTargets.47 Id="22243">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.47>
|
||||
<ControllerTargets.48 Id="22244">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.48>
|
||||
<ControllerTargets.49 Id="22245">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.49>
|
||||
<ControllerTargets.50 Id="22246">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.50>
|
||||
<ControllerTargets.51 Id="22247">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.51>
|
||||
<ControllerTargets.52 Id="22248">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.52>
|
||||
<ControllerTargets.53 Id="22249">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.53>
|
||||
<ControllerTargets.54 Id="22250">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.54>
|
||||
<ControllerTargets.55 Id="22251">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.55>
|
||||
<ControllerTargets.56 Id="22252">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.56>
|
||||
<ControllerTargets.57 Id="22253">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.57>
|
||||
<ControllerTargets.58 Id="22254">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.58>
|
||||
<ControllerTargets.59 Id="22255">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.59>
|
||||
<ControllerTargets.60 Id="22256">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.60>
|
||||
<ControllerTargets.61 Id="22257">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.61>
|
||||
<ControllerTargets.62 Id="22258">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.62>
|
||||
<ControllerTargets.63 Id="22259">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.63>
|
||||
<ControllerTargets.64 Id="22260">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.64>
|
||||
<ControllerTargets.65 Id="22261">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.65>
|
||||
<ControllerTargets.66 Id="22262">
|
||||
<LockEnvelope Value="1" />
|
||||
</ControllerTargets.66>
|
||||
<ControllerTargets.67 Id="22263">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.67>
|
||||
<ControllerTargets.68 Id="22264">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.68>
|
||||
<ControllerTargets.69 Id="22265">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.69>
|
||||
<ControllerTargets.70 Id="22266">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.70>
|
||||
<ControllerTargets.71 Id="22267">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.71>
|
||||
<ControllerTargets.72 Id="22268">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.72>
|
||||
<ControllerTargets.73 Id="22269">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.73>
|
||||
<ControllerTargets.74 Id="22270">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.74>
|
||||
<ControllerTargets.75 Id="22271">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.75>
|
||||
<ControllerTargets.76 Id="22272">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.76>
|
||||
<ControllerTargets.77 Id="22273">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.77>
|
||||
<ControllerTargets.78 Id="22274">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.78>
|
||||
<ControllerTargets.79 Id="22275">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.79>
|
||||
<ControllerTargets.80 Id="22276">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.80>
|
||||
<ControllerTargets.81 Id="22277">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.81>
|
||||
<ControllerTargets.82 Id="22278">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.82>
|
||||
<ControllerTargets.83 Id="22279">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.83>
|
||||
<ControllerTargets.84 Id="22280">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.84>
|
||||
<ControllerTargets.85 Id="22281">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.85>
|
||||
<ControllerTargets.86 Id="22282">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.86>
|
||||
<ControllerTargets.87 Id="22283">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.87>
|
||||
<ControllerTargets.88 Id="22284">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.88>
|
||||
<ControllerTargets.89 Id="22285">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.89>
|
||||
<ControllerTargets.90 Id="22286">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.90>
|
||||
<ControllerTargets.91 Id="22287">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.91>
|
||||
<ControllerTargets.92 Id="22288">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.92>
|
||||
<ControllerTargets.93 Id="22289">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.93>
|
||||
<ControllerTargets.94 Id="22290">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.94>
|
||||
<ControllerTargets.95 Id="22291">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.95>
|
||||
<ControllerTargets.96 Id="22292">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.96>
|
||||
<ControllerTargets.97 Id="22293">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.97>
|
||||
<ControllerTargets.98 Id="22294">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.98>
|
||||
<ControllerTargets.99 Id="22295">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.99>
|
||||
<ControllerTargets.100 Id="22296">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.100>
|
||||
<ControllerTargets.101 Id="22297">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.101>
|
||||
<ControllerTargets.102 Id="22298">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.102>
|
||||
<ControllerTargets.103 Id="22299">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.103>
|
||||
<ControllerTargets.104 Id="22300">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.104>
|
||||
<ControllerTargets.105 Id="22301">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.105>
|
||||
<ControllerTargets.106 Id="22302">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.106>
|
||||
<ControllerTargets.107 Id="22303">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.107>
|
||||
<ControllerTargets.108 Id="22304">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.108>
|
||||
<ControllerTargets.109 Id="22305">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.109>
|
||||
<ControllerTargets.110 Id="22306">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.110>
|
||||
<ControllerTargets.111 Id="22307">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.111>
|
||||
<ControllerTargets.112 Id="22308">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.112>
|
||||
<ControllerTargets.113 Id="22309">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.113>
|
||||
<ControllerTargets.114 Id="22310">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.114>
|
||||
<ControllerTargets.115 Id="22311">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.115>
|
||||
<ControllerTargets.116 Id="22312">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.116>
|
||||
<ControllerTargets.117 Id="22313">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.117>
|
||||
<ControllerTargets.118 Id="22314">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.118>
|
||||
<ControllerTargets.119 Id="22315">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.119>
|
||||
<ControllerTargets.120 Id="22316">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.120>
|
||||
<ControllerTargets.121 Id="22317">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.121>
|
||||
<ControllerTargets.122 Id="22318">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.122>
|
||||
<ControllerTargets.123 Id="22319">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.123>
|
||||
<ControllerTargets.124 Id="22320">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.124>
|
||||
<ControllerTargets.125 Id="22321">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.125>
|
||||
<ControllerTargets.126 Id="22322">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.126>
|
||||
<ControllerTargets.127 Id="22323">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.127>
|
||||
<ControllerTargets.128 Id="22324">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.128>
|
||||
<ControllerTargets.129 Id="22325">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.129>
|
||||
<ControllerTargets.130 Id="22326">
|
||||
<LockEnvelope Value="0" />
|
||||
</ControllerTargets.130>
|
||||
</MidiControllers>
|
||||
</MainSequencer>
|
||||
<FreezeSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<BreakoutIsExpanded Value="false" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="22327">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="22328" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList>
|
||||
<ClipSlot Id="0">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="1">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="2">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="3">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="4">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="5">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="6">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
<ClipSlot Id="7">
|
||||
<LomId Value="0" />
|
||||
<ClipSlot>
|
||||
<Value />
|
||||
</ClipSlot>
|
||||
<HasStop Value="true" />
|
||||
<NeedRefreeze Value="true" />
|
||||
</ClipSlot>
|
||||
</ClipSlotList>
|
||||
<MonitoringEnum Value="1" />
|
||||
<Sample>
|
||||
<ArrangerAutomation>
|
||||
<Events />
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</Sample>
|
||||
<VolumeModulationTarget Id="22329">
|
||||
<LockEnvelope Value="0" />
|
||||
</VolumeModulationTarget>
|
||||
<TranspositionModulationTarget Id="22330">
|
||||
<LockEnvelope Value="0" />
|
||||
</TranspositionModulationTarget>
|
||||
<GrainSizeModulationTarget Id="22332">
|
||||
<LockEnvelope Value="0" />
|
||||
</GrainSizeModulationTarget>
|
||||
<FluxModulationTarget Id="22333">
|
||||
<LockEnvelope Value="0" />
|
||||
</FluxModulationTarget>
|
||||
<SampleOffsetModulationTarget Id="22334">
|
||||
<LockEnvelope Value="0" />
|
||||
</SampleOffsetModulationTarget>
|
||||
<PitchViewScrollPosition Value="-1073741824" />
|
||||
<SampleOffsetModulationScrollPosition Value="-1073741824" />
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="1" />
|
||||
</Recorder>
|
||||
</FreezeSequencer>
|
||||
<DeviceChain>
|
||||
<Devices />
|
||||
<SignalModulations />
|
||||
</DeviceChain>
|
||||
</DeviceChain>
|
||||
<ReWireSlaveMidiTargetId Value="0" />
|
||||
<PitchbendRange Value="96" />
|
||||
</MidiTrack>
|
||||
1049
src/lib/exporters/ableton/templates/project.xml
Normal file
285
src/lib/exporters/ableton/templates/returnTrack.xml
Normal file
@@ -0,0 +1,285 @@
|
||||
<ReturnTrack Id="-1">
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<PreferredContentViewMode Value="0" />
|
||||
<TrackDelay>
|
||||
<Value Value="0" />
|
||||
<IsValueSampleBased Value="false" />
|
||||
</TrackDelay>
|
||||
<Name>
|
||||
<EffectiveName Value="A-Return" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<MemorizedFirstClipName Value="" />
|
||||
</Name>
|
||||
<Color Value="16" />
|
||||
<AutomationEnvelopes>
|
||||
<Envelopes />
|
||||
</AutomationEnvelopes>
|
||||
<TrackGroupId Value="-1" />
|
||||
<TrackUnfolded Value="false" />
|
||||
<DevicesListWrapper LomId="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
<ViewData Value="{}" />
|
||||
<TakeLanes>
|
||||
<TakeLanes />
|
||||
<AreTakeLanesFolded Value="true" />
|
||||
</TakeLanes>
|
||||
<LinkedTrackGroupId Value="-1" />
|
||||
<DeviceChain>
|
||||
<AutomationLanes>
|
||||
<AutomationLanes>
|
||||
<AutomationLane Id="0">
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<IsContentSelectedInDocument Value="false" />
|
||||
<LaneHeight Value="68" />
|
||||
</AutomationLane>
|
||||
</AutomationLanes>
|
||||
<AreAdditionalAutomationLanesFolded Value="false" />
|
||||
</AutomationLanes>
|
||||
<ClipEnvelopeChooserViewState>
|
||||
<SelectedDevice Value="0" />
|
||||
<SelectedEnvelope Value="0" />
|
||||
<PreferModulationVisible Value="false" />
|
||||
</ClipEnvelopeChooserViewState>
|
||||
<AudioInputRouting>
|
||||
<Target Value="AudioIn/External/S0" />
|
||||
<UpperDisplayString Value="Ext. In" />
|
||||
<LowerDisplayString Value="1/2" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioInputRouting>
|
||||
<MidiInputRouting>
|
||||
<Target Value="MidiIn/External.All/-1" />
|
||||
<UpperDisplayString Value="Ext: All Ins" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiInputRouting>
|
||||
<AudioOutputRouting>
|
||||
<Target Value="AudioOut/Master" />
|
||||
<UpperDisplayString Value="Master" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</AudioOutputRouting>
|
||||
<MidiOutputRouting>
|
||||
<Target Value="MidiOut/None" />
|
||||
<UpperDisplayString Value="None" />
|
||||
<LowerDisplayString Value="" />
|
||||
<MpeSettings>
|
||||
<ZoneType Value="0" />
|
||||
<FirstNoteChannel Value="1" />
|
||||
<LastNoteChannel Value="15" />
|
||||
</MpeSettings>
|
||||
</MidiOutputRouting>
|
||||
<Mixer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23801">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23802" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<Sends>
|
||||
<TrackSendHolder Id="0">
|
||||
<Send>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23830">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23831">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Send>
|
||||
<Active Value="false" />
|
||||
</TrackSendHolder>
|
||||
</Sends>
|
||||
<Speaker>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23803">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</Speaker>
|
||||
<SoloSink Value="false" />
|
||||
<PanMode Value="0" />
|
||||
<Pan>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23804">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23805">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Pan>
|
||||
<SplitStereoPanL>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="-1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23806">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23807">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanL>
|
||||
<SplitStereoPanR>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="-1" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23808">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23809">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</SplitStereoPanR>
|
||||
<Volume>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="2" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23810">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23811">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Volume>
|
||||
<ViewStateSesstionTrackWidth Value="93" />
|
||||
<CrossFadeState>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="1" />
|
||||
<AutomationTarget Id="23812">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
</CrossFadeState>
|
||||
<SendsListWrapper LomId="0" />
|
||||
</Mixer>
|
||||
<DeviceChain>
|
||||
<Devices />
|
||||
<SignalModulations />
|
||||
</DeviceChain>
|
||||
<FreezeSequencer>
|
||||
<LomId Value="0" />
|
||||
<LomIdView Value="0" />
|
||||
<IsExpanded Value="true" />
|
||||
<On>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="true" />
|
||||
<AutomationTarget Id="23813">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<MidiCCOnOffThresholds>
|
||||
<Min Value="64" />
|
||||
<Max Value="127" />
|
||||
</MidiCCOnOffThresholds>
|
||||
</On>
|
||||
<ModulationSourceCount Value="0" />
|
||||
<ParametersListWrapper LomId="0" />
|
||||
<Pointee Id="23814" />
|
||||
<LastSelectedTimeableIndex Value="0" />
|
||||
<LastSelectedClipEnvelopeIndex Value="0" />
|
||||
<LastPresetRef>
|
||||
<Value />
|
||||
</LastPresetRef>
|
||||
<LockedScripts />
|
||||
<IsFolded Value="false" />
|
||||
<ShouldShowPresetName Value="true" />
|
||||
<UserName Value="" />
|
||||
<Annotation Value="" />
|
||||
<SourceContext>
|
||||
<Value />
|
||||
</SourceContext>
|
||||
<ClipSlotList />
|
||||
<MonitoringEnum Value="1" />
|
||||
<Sample>
|
||||
<ArrangerAutomation>
|
||||
<Events />
|
||||
<AutomationTransformViewState>
|
||||
<IsTransformPending Value="false" />
|
||||
<TimeAndValueTransforms />
|
||||
</AutomationTransformViewState>
|
||||
</ArrangerAutomation>
|
||||
</Sample>
|
||||
<VolumeModulationTarget Id="23815">
|
||||
<LockEnvelope Value="0" />
|
||||
</VolumeModulationTarget>
|
||||
<TranspositionModulationTarget Id="23816">
|
||||
<LockEnvelope Value="0" />
|
||||
</TranspositionModulationTarget>
|
||||
<GrainSizeModulationTarget Id="23817">
|
||||
<LockEnvelope Value="0" />
|
||||
</GrainSizeModulationTarget>
|
||||
<FluxModulationTarget Id="23818">
|
||||
<LockEnvelope Value="0" />
|
||||
</FluxModulationTarget>
|
||||
<SampleOffsetModulationTarget Id="23819">
|
||||
<LockEnvelope Value="0" />
|
||||
</SampleOffsetModulationTarget>
|
||||
<PitchViewScrollPosition Value="-1073741824" />
|
||||
<SampleOffsetModulationScrollPosition Value="-1073741824" />
|
||||
<Recorder>
|
||||
<IsArmed Value="false" />
|
||||
<TakeCounter Value="1" />
|
||||
</Recorder>
|
||||
</FreezeSequencer>
|
||||
</DeviceChain>
|
||||
</ReturnTrack>
|
||||
23
src/lib/exporters/ableton/templates/scene.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<Scene Id="0">
|
||||
<FollowAction>
|
||||
<FollowTime Value="0" />
|
||||
<IsLinked Value="true" />
|
||||
<LoopIterations Value="1" />
|
||||
<FollowActionA Value="4" />
|
||||
<FollowActionB Value="0" />
|
||||
<FollowChanceA Value="100" />
|
||||
<FollowChanceB Value="0" />
|
||||
<JumpIndexA Value="0" />
|
||||
<JumpIndexB Value="0" />
|
||||
<FollowActionEnabled Value="false" />
|
||||
</FollowAction>
|
||||
<Name Value="" />
|
||||
<Annotation Value="" />
|
||||
<Color Value="-1" />
|
||||
<Tempo Value="120" />
|
||||
<IsTempoEnabled Value="false" />
|
||||
<TimeSignatureId Value="201" />
|
||||
<IsTimeSignatureEnabled Value="false" />
|
||||
<LomId Value="0" />
|
||||
<ClipSlotsListWrapper LomId="0" />
|
||||
</Scene>
|
||||
1815
src/lib/exporters/ableton/templates/simpler.xml
Normal file
17
src/lib/exporters/ableton/templates/trackSendHolder.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<TrackSendHolder Id="1">
|
||||
<Send>
|
||||
<LomId Value="0" />
|
||||
<Manual Value="0" />
|
||||
<MidiControllerRange>
|
||||
<Min Value="0" />
|
||||
<Max Value="1" />
|
||||
</MidiControllerRange>
|
||||
<AutomationTarget Id="23828">
|
||||
<LockEnvelope Value="0" />
|
||||
</AutomationTarget>
|
||||
<ModulationTarget Id="23829">
|
||||
<LockEnvelope Value="0" />
|
||||
</ModulationTarget>
|
||||
</Send>
|
||||
<Active Value="true" />
|
||||
</TrackSendHolder>
|
||||
77
src/lib/exporters/ableton/types/common.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
export interface ValueElement {
|
||||
'@Value': any;
|
||||
}
|
||||
|
||||
export interface LomIdElement {
|
||||
LomId: ValueElement;
|
||||
}
|
||||
|
||||
export interface AutomationTarget {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
}
|
||||
|
||||
export interface MidiCCOnOffThresholds {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
}
|
||||
|
||||
export interface On {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
export interface MidiControllerRange {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
}
|
||||
|
||||
export interface ModulationTarget {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
}
|
||||
|
||||
export interface FileRef {
|
||||
RelativePathType: ValueElement;
|
||||
RelativePath: ValueElement;
|
||||
Path: ValueElement;
|
||||
Type: ValueElement;
|
||||
LivePackName: ValueElement;
|
||||
LivePackId: ValueElement;
|
||||
OriginalFileSize: ValueElement;
|
||||
OriginalCrc: ValueElement;
|
||||
}
|
||||
|
||||
export interface AbletonDefaultPresetRef {
|
||||
'@Id': number;
|
||||
FileRef: FileRef;
|
||||
DeviceId: {
|
||||
'@Name': string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LastPresetRef {
|
||||
Value: AbletonDefaultPresetRef;
|
||||
}
|
||||
|
||||
export interface OriginalFileRef {
|
||||
FileRef: FileRef;
|
||||
}
|
||||
|
||||
export interface PresetRef {
|
||||
AbletonDefaultPresetRef: AbletonDefaultPresetRef;
|
||||
}
|
||||
|
||||
export interface BranchSourceContext {
|
||||
'@Id': number;
|
||||
OriginalFileRef: OriginalFileRef;
|
||||
BrowserContentPath: ValueElement;
|
||||
PresetRef: PresetRef;
|
||||
BranchDeviceId: ValueElement;
|
||||
}
|
||||
|
||||
export interface SourceContext {
|
||||
Value: BranchSourceContext;
|
||||
}
|
||||
127
src/lib/exporters/ableton/types/drumBranch.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface MidiToAudioDeviceChain {
|
||||
'@Id': number;
|
||||
Devices: {};
|
||||
SignalModulations: {};
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
MidiToAudioDeviceChain: MidiToAudioDeviceChain;
|
||||
}
|
||||
|
||||
interface BranchSelectorRange {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
}
|
||||
|
||||
interface Volume {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Panorama {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MpeSettings {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
}
|
||||
|
||||
interface Routable {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: MpeSettings;
|
||||
}
|
||||
|
||||
interface RoutingHelper {
|
||||
Routable: Routable;
|
||||
TargetEnum: ValueElement;
|
||||
}
|
||||
|
||||
interface MixerDevice {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Speaker: On;
|
||||
Volume: Volume;
|
||||
Panorama: Panorama;
|
||||
SendInfos: {};
|
||||
RoutingHelper: RoutingHelper;
|
||||
SendsListWrapper: LomIdElement;
|
||||
}
|
||||
|
||||
interface BranchInfo {
|
||||
ReceivingNote: ValueElement;
|
||||
SendingNote: ValueElement;
|
||||
ChokeGroup: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSDrumBranchContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
Name: Name;
|
||||
IsSelected: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
BranchSelectorRange: BranchSelectorRange;
|
||||
IsSoloed: ValueElement;
|
||||
SessionViewBranchWidth: ValueElement;
|
||||
IsHighlightedInSessionView: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
Color: ValueElement;
|
||||
AutoColored: ValueElement;
|
||||
AutoColorScheme: ValueElement;
|
||||
SoloActivatedInSessionMixer: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
MixerDevice: MixerDevice;
|
||||
BranchInfo: BranchInfo;
|
||||
}
|
||||
|
||||
export interface ALSDrumBranch {
|
||||
DrumBranch: ALSDrumBranchContent;
|
||||
}
|
||||
97
src/lib/exporters/ableton/types/drumRack.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import { ALSDrumBranchContent } from './drumBranch';
|
||||
|
||||
interface MacroControl {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ChainSelector {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MacroVariations {
|
||||
MacroSnapshots: {};
|
||||
}
|
||||
|
||||
export interface ALSDrumRackContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Branches: {
|
||||
DrumBranch: ALSDrumBranchContent[];
|
||||
};
|
||||
IsBranchesListVisible: ValueElement;
|
||||
IsReturnBranchesListVisible: ValueElement;
|
||||
IsRangesEditorVisible: ValueElement;
|
||||
AreDevicesVisible: ValueElement;
|
||||
NumVisibleMacroControls: ValueElement;
|
||||
MacroControls: { [key: string]: MacroControl };
|
||||
MacroDisplayNames: { [key: string]: ValueElement };
|
||||
MacroDefaults: { [key: string]: ValueElement };
|
||||
MacroAnnotations: { [key: string]: ValueElement };
|
||||
ForceDisplayGenericValue: { [key: string]: ValueElement };
|
||||
AreMacroControlsVisible: ValueElement;
|
||||
IsAutoSelectEnabled: ValueElement;
|
||||
ChainSelector: ChainSelector;
|
||||
ChainSelectorRelativePosition: ValueElement;
|
||||
ViewsToRestoreWhenUnfolding: ValueElement;
|
||||
ReturnBranches: {};
|
||||
BranchesSplitterProportion: ValueElement;
|
||||
ShowBranchesInSessionMixer: ValueElement;
|
||||
MacroColor: { [key: string]: ValueElement };
|
||||
LockId: ValueElement;
|
||||
LockSeal: ValueElement;
|
||||
ChainsListWrapper: LomIdElement;
|
||||
ReturnChainsListWrapper: LomIdElement;
|
||||
MacroVariations: MacroVariations;
|
||||
ExcludeMacroFromRandomization: { [key: string]: ValueElement };
|
||||
ExcludeMacroFromSnapshots: { [key: string]: ValueElement };
|
||||
AreMacroVariationsControlsVisible: ValueElement;
|
||||
ChainSelectorFilterMidiCtrl: ValueElement;
|
||||
RangeTypeIndex: ValueElement;
|
||||
ShowsZonesInsteadOfNoteNames: ValueElement;
|
||||
IsMidiSectionVisible: ValueElement;
|
||||
AreSendsVisible: ValueElement;
|
||||
ArePadsVisible: ValueElement;
|
||||
PadScrollPosition: ValueElement;
|
||||
DrumPadsListWrapper: LomIdElement;
|
||||
VisibleDrumPadsListWrapper: LomIdElement;
|
||||
}
|
||||
|
||||
export interface ALSDrumRack {
|
||||
DrumGroupDevice: ALSDrumRackContent;
|
||||
}
|
||||
151
src/lib/exporters/ableton/types/effectChorus.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface Mode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Shaping {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Rate {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Amount {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Feedback {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface InvertFeedback {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface VibratoOffset {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface HighpassEnabled {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface HighpassFrequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Width {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Warmth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface OutputGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSChorusContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Mode: Mode;
|
||||
Shaping: Shaping;
|
||||
Rate: Rate;
|
||||
Amount: Amount;
|
||||
Feedback: Feedback;
|
||||
InvertFeedback: InvertFeedback;
|
||||
VibratoOffset: VibratoOffset;
|
||||
HighpassEnabled: HighpassEnabled;
|
||||
HighpassFrequency: HighpassFrequency;
|
||||
Width: Width;
|
||||
Warmth: Warmth;
|
||||
OutputGain: OutputGain;
|
||||
DryWet: DryWet;
|
||||
}
|
||||
|
||||
export interface ALSChorus {
|
||||
Chorus2: ALSChorusContent;
|
||||
}
|
||||
249
src/lib/exporters/ableton/types/effectCompressor.ts
Normal file
@@ -0,0 +1,249 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface Threshold {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Ratio {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ExpansionRatio {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Attack {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Release {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface AutoReleaseControlOnOff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Gain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface GainCompensation {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Model {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface LegacyModel {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface LogEnvelope {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface LegacyEnvFollowerMode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Knee {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface LookAhead {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface SideListen {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface MpeSettings {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
}
|
||||
|
||||
interface Routable {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: MpeSettings;
|
||||
}
|
||||
|
||||
interface Volume {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RoutedInput {
|
||||
Routable: Routable;
|
||||
Volume: Volume;
|
||||
}
|
||||
|
||||
interface OnOff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface SideChain {
|
||||
OnOff: OnOff;
|
||||
RoutedInput: RoutedInput;
|
||||
DryWet: DryWet;
|
||||
}
|
||||
|
||||
interface Mode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Freq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Q {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SideChainEq {
|
||||
On: On;
|
||||
Mode: Mode;
|
||||
Freq: Freq;
|
||||
Q: Q;
|
||||
Gain: Gain;
|
||||
}
|
||||
|
||||
export interface ALSCompressorContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Threshold: Threshold;
|
||||
Ratio: Ratio;
|
||||
ExpansionRatio: ExpansionRatio;
|
||||
Attack: Attack;
|
||||
Release: Release;
|
||||
AutoReleaseControlOnOff: AutoReleaseControlOnOff;
|
||||
Gain: Gain;
|
||||
GainCompensation: GainCompensation;
|
||||
DryWet: DryWet;
|
||||
Model: Model;
|
||||
LegacyModel: LegacyModel;
|
||||
LogEnvelope: LogEnvelope;
|
||||
LegacyEnvFollowerMode: LegacyEnvFollowerMode;
|
||||
Knee: Knee;
|
||||
LookAhead: LookAhead;
|
||||
SideListen: SideListen;
|
||||
SideChain: SideChain;
|
||||
SideChainEq: SideChainEq;
|
||||
Live8LegacyMode: ValueElement;
|
||||
ViewMode: ValueElement;
|
||||
IsOutputCurveVisible: ValueElement;
|
||||
RmsTimeShort: ValueElement;
|
||||
RmsTimeLong: ValueElement;
|
||||
ReleaseTimeShort: ValueElement;
|
||||
ReleaseTimeLong: ValueElement;
|
||||
CrossfaderSmoothingTime: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSCompressor {
|
||||
Compressor2: ALSCompressorContent;
|
||||
}
|
||||
245
src/lib/exporters/ableton/types/effectDelay.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface DelayLine_SmoothingMode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_Link {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_PingPong {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface DelayLine_TimeL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_TimeR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SimpleDelayTimeL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SimpleDelayTimeR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_PingPongDelayTimeL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_PingPongDelayTimeR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncedSixteenthL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_SyncedSixteenthR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_OffsetL {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DelayLine_OffsetR {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Feedback {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Freeze {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Filter_On {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Filter_Frequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Filter_Bandwidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Modulation_Frequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Modulation_AmountTime {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Modulation_AmountFilter {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSDelayContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
DelayLine_SmoothingMode: DelayLine_SmoothingMode;
|
||||
DelayLine_Link: DelayLine_Link;
|
||||
DelayLine_PingPong: DelayLine_PingPong;
|
||||
DelayLine_SyncL: DelayLine_SyncL;
|
||||
DelayLine_SyncR: DelayLine_SyncR;
|
||||
DelayLine_TimeL: DelayLine_TimeL;
|
||||
DelayLine_TimeR: DelayLine_TimeR;
|
||||
DelayLine_SimpleDelayTimeL: DelayLine_SimpleDelayTimeL;
|
||||
DelayLine_SimpleDelayTimeR: DelayLine_SimpleDelayTimeR;
|
||||
DelayLine_PingPongDelayTimeL: DelayLine_PingPongDelayTimeL;
|
||||
DelayLine_PingPongDelayTimeR: DelayLine_PingPongDelayTimeR;
|
||||
DelayLine_SyncedSixteenthL: DelayLine_SyncedSixteenthL;
|
||||
DelayLine_SyncedSixteenthR: DelayLine_SyncedSixteenthR;
|
||||
DelayLine_OffsetL: DelayLine_OffsetL;
|
||||
DelayLine_OffsetR: DelayLine_OffsetR;
|
||||
DelayLine_CompatibilityMode: ValueElement;
|
||||
Feedback: Feedback;
|
||||
Freeze: Freeze;
|
||||
Filter_On: Filter_On;
|
||||
Filter_Frequency: Filter_Frequency;
|
||||
Filter_Bandwidth: Filter_Bandwidth;
|
||||
Modulation_Frequency: Modulation_Frequency;
|
||||
Modulation_AmountTime: Modulation_AmountTime;
|
||||
Modulation_AmountFilter: Modulation_AmountFilter;
|
||||
DryWet: DryWet;
|
||||
DryWetMode: ValueElement;
|
||||
EcoProcessing: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSDelay {
|
||||
Delay: ALSDelayContent;
|
||||
}
|
||||
91
src/lib/exporters/ableton/types/effectDistortion.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface MidFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface BandWidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Drive {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Tone {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface PreserveDynamics {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSDistortionContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
MidFreq: MidFreq;
|
||||
BandWidth: BandWidth;
|
||||
Drive: Drive;
|
||||
DryWet: DryWet;
|
||||
Tone: Tone;
|
||||
PreserveDynamics: PreserveDynamics;
|
||||
}
|
||||
|
||||
export interface ALSDistortion {
|
||||
Overdrive: ALSDistortionContent;
|
||||
}
|
||||
307
src/lib/exporters/ableton/types/effectFilter.ts
Normal file
@@ -0,0 +1,307 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface LegacyFilterType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface FilterType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface CircuitLpHp {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface CircuitBpNoMo {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Slope {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Cutoff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface LegacyQ {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Resonance {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Morph {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Drive {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ModHub {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Attack {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Release {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface LfoAmount {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Type {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Frequency {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RateType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface BeatRate {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface StereoMode {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface Spin {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Phase {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Offset {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface IsOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Quantize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BeatQuantize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface NoiseWidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface Lfo {
|
||||
Type: Type;
|
||||
Frequency: Frequency;
|
||||
RateType: RateType;
|
||||
BeatRate: BeatRate;
|
||||
StereoMode: StereoMode;
|
||||
Spin: Spin;
|
||||
Phase: Phase;
|
||||
Offset: Offset;
|
||||
IsOn: IsOn;
|
||||
Quantize: Quantize;
|
||||
BeatQuantize: BeatQuantize;
|
||||
NoiseWidth: NoiseWidth;
|
||||
}
|
||||
|
||||
interface MpeSettings {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
}
|
||||
|
||||
interface Routable {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: MpeSettings;
|
||||
}
|
||||
|
||||
interface Volume {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RoutedInput {
|
||||
Routable: Routable;
|
||||
Volume: Volume;
|
||||
}
|
||||
|
||||
interface DryWet {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface OnOff {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface SideChain {
|
||||
OnOff: OnOff;
|
||||
RoutedInput: RoutedInput;
|
||||
DryWet: DryWet;
|
||||
}
|
||||
|
||||
export interface ALSFilterContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
LegacyMode: ValueElement;
|
||||
LegacyFilterType: LegacyFilterType;
|
||||
FilterType: FilterType;
|
||||
CircuitLpHp: CircuitLpHp;
|
||||
CircuitBpNoMo: CircuitBpNoMo;
|
||||
Slope: Slope;
|
||||
Cutoff: Cutoff;
|
||||
CutoffLimit: ValueElement;
|
||||
LegacyQ: LegacyQ;
|
||||
Resonance: Resonance;
|
||||
Morph: Morph;
|
||||
Drive: Drive;
|
||||
ModHub: ModHub;
|
||||
Attack: Attack;
|
||||
Release: Release;
|
||||
LfoAmount: LfoAmount;
|
||||
Lfo: Lfo;
|
||||
SideChain: SideChain;
|
||||
}
|
||||
|
||||
export interface ALSFilter {
|
||||
AutoFilter: ALSFilterContent;
|
||||
}
|
||||
312
src/lib/exporters/ableton/types/effectReverb.ts
Normal file
@@ -0,0 +1,312 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LastPresetRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
On,
|
||||
SourceContext,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface PreDelay {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface BandHighOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BandLowOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BandFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface BandWidth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SpinOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface EarlyReflectModFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface EarlyReflectModDepth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DiffuseDelay {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfHighOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface HighFilterType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface ShelfHiFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfHiGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfLowOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface ShelfLoFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ShelfLoGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface ChorusOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface SizeModFreq {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SizeModDepth {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface DecayTime {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface AllPassGain {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface AllPassSize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface FreezeOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface FlatOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface CutOn {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
MidiCCOnOffThresholds: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface RoomSize {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface SizeSmoothing {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface StereoSeparation {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface RoomType {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
AutomationTarget: AutomationTarget;
|
||||
}
|
||||
|
||||
interface MixReflect {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MixDiffuse {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
interface MixDirect {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSReverbContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: On;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
PreDelay: PreDelay;
|
||||
BandHighOn: BandHighOn;
|
||||
BandLowOn: BandLowOn;
|
||||
BandFreq: BandFreq;
|
||||
BandWidth: BandWidth;
|
||||
SpinOn: SpinOn;
|
||||
EarlyReflectModFreq: EarlyReflectModFreq;
|
||||
EarlyReflectModDepth: EarlyReflectModDepth;
|
||||
DiffuseDelay: DiffuseDelay;
|
||||
ShelfHighOn: ShelfHighOn;
|
||||
HighFilterType: HighFilterType;
|
||||
ShelfHiFreq: ShelfHiFreq;
|
||||
ShelfHiGain: ShelfHiGain;
|
||||
ShelfLowOn: ShelfLowOn;
|
||||
ShelfLoFreq: ShelfLoFreq;
|
||||
ShelfLoGain: ShelfLoGain;
|
||||
ChorusOn: ChorusOn;
|
||||
SizeModFreq: SizeModFreq;
|
||||
SizeModDepth: SizeModDepth;
|
||||
DecayTime: DecayTime;
|
||||
AllPassGain: AllPassGain;
|
||||
AllPassSize: AllPassSize;
|
||||
FreezeOn: FreezeOn;
|
||||
FlatOn: FlatOn;
|
||||
CutOn: CutOn;
|
||||
RoomSize: RoomSize;
|
||||
SizeSmoothing: SizeSmoothing;
|
||||
StereoSeparation: StereoSeparation;
|
||||
RoomType: RoomType;
|
||||
MixReflect: MixReflect;
|
||||
MixDiffuse: MixDiffuse;
|
||||
MixDirect: MixDirect;
|
||||
StereoSeparationOnDrySignal: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSReverb {
|
||||
Reverb: ALSReverbContent;
|
||||
}
|
||||
65
src/lib/exporters/ableton/types/groupTrack.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { LomIdElement, ValueElement } from './common';
|
||||
import { Mixer } from './midiTrack';
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackDelay {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface GroupTrackSlot {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
}
|
||||
|
||||
interface Slots {
|
||||
GroupTrackSlot: GroupTrackSlot[];
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
Mixer: Mixer;
|
||||
DeviceChain: {
|
||||
Devices: {
|
||||
'#': Array<any>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSGroupTrackContent {
|
||||
'@Id': number;
|
||||
'@_internalId'?: string;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: TrackDelay;
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: {
|
||||
Envelopes: {};
|
||||
};
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
ClipSlotsListWrapper: LomIdElement;
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
Slots: Slots;
|
||||
DeviceChain: DeviceChain;
|
||||
}
|
||||
|
||||
export interface ALSGroupTrack {
|
||||
GroupTrack: ALSGroupTrackContent;
|
||||
}
|
||||
136
src/lib/exporters/ableton/types/midiClip.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
interface ValueElement {
|
||||
'@Value': any;
|
||||
}
|
||||
|
||||
interface MidiNoteEvent {
|
||||
'@Time': number;
|
||||
'@Duration': number;
|
||||
'@Velocity': number;
|
||||
'@OffVelocity': number;
|
||||
'@NoteId': number;
|
||||
}
|
||||
|
||||
interface KeyTrack {
|
||||
'@Id': number;
|
||||
Notes: {
|
||||
MidiNoteEvent: MidiNoteEvent[];
|
||||
};
|
||||
MidiKey: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSMidiClipContent {
|
||||
'@Id': number;
|
||||
'@Time': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
CurrentStart: ValueElement;
|
||||
CurrentEnd: ValueElement;
|
||||
Loop: {
|
||||
LoopStart: ValueElement;
|
||||
LoopEnd: ValueElement;
|
||||
StartRelative: ValueElement;
|
||||
LoopOn: ValueElement;
|
||||
OutMarker: ValueElement;
|
||||
HiddenLoopStart: ValueElement;
|
||||
HiddenLoopEnd: ValueElement;
|
||||
};
|
||||
Name: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
Color: ValueElement;
|
||||
LaunchMode: ValueElement;
|
||||
LaunchQuantisation: ValueElement;
|
||||
TimeSignature: {
|
||||
TimeSignatures: {
|
||||
RemoteableTimeSignature: {
|
||||
'@Id': number;
|
||||
Numerator: ValueElement;
|
||||
Denominator: ValueElement;
|
||||
Time: ValueElement;
|
||||
};
|
||||
};
|
||||
};
|
||||
Envelopes: {
|
||||
Envelopes: {};
|
||||
};
|
||||
ScrollerTimePreserver: {
|
||||
LeftTime: ValueElement;
|
||||
RightTime: ValueElement;
|
||||
};
|
||||
TimeSelection: {
|
||||
AnchorTime: ValueElement;
|
||||
OtherTime: ValueElement;
|
||||
};
|
||||
Legato: ValueElement;
|
||||
Ram: ValueElement;
|
||||
GrooveSettings: {
|
||||
GrooveId: ValueElement;
|
||||
};
|
||||
Disabled: ValueElement;
|
||||
VelocityAmount: ValueElement;
|
||||
FollowAction: {
|
||||
FollowTime: ValueElement;
|
||||
IsLinked: ValueElement;
|
||||
LoopIterations: ValueElement;
|
||||
FollowActionA: ValueElement;
|
||||
FollowActionB: ValueElement;
|
||||
FollowChanceA: ValueElement;
|
||||
FollowChanceB: ValueElement;
|
||||
JumpIndexA: ValueElement;
|
||||
JumpIndexB: ValueElement;
|
||||
FollowActionEnabled: ValueElement;
|
||||
};
|
||||
Grid: {
|
||||
FixedNumerator: ValueElement;
|
||||
FixedDenominator: ValueElement;
|
||||
GridIntervalPixel: ValueElement;
|
||||
Ntoles: ValueElement;
|
||||
SnapToGrid: ValueElement;
|
||||
Fixed: ValueElement;
|
||||
};
|
||||
FreezeStart: ValueElement;
|
||||
FreezeEnd: ValueElement;
|
||||
IsWarped: ValueElement;
|
||||
TakeId: ValueElement;
|
||||
IsInKey: ValueElement;
|
||||
ScaleInformation: {
|
||||
Root: ValueElement;
|
||||
Name: ValueElement;
|
||||
};
|
||||
Notes: {
|
||||
KeyTracks: {
|
||||
KeyTrack: KeyTrack[];
|
||||
};
|
||||
PerNoteEventStore: {
|
||||
EventLists: {};
|
||||
};
|
||||
NoteProbabilityGroups: {};
|
||||
ProbabilityGroupIdGenerator: {
|
||||
NextId: ValueElement;
|
||||
};
|
||||
NoteIdGenerator: {
|
||||
NextId: ValueElement;
|
||||
};
|
||||
};
|
||||
BankSelectCoarse: ValueElement;
|
||||
BankSelectFine: ValueElement;
|
||||
ProgramChange: ValueElement;
|
||||
NoteEditorFoldInZoom: ValueElement;
|
||||
NoteEditorFoldInScroll: ValueElement;
|
||||
NoteEditorFoldOutZoom: ValueElement;
|
||||
NoteEditorFoldOutScroll: ValueElement;
|
||||
NoteSpellingPreference: ValueElement;
|
||||
AccidentalSpellingPreference: ValueElement;
|
||||
PreferFlatRootNote: ValueElement;
|
||||
ExpressionGrid: {
|
||||
FixedNumerator: ValueElement;
|
||||
FixedDenominator: ValueElement;
|
||||
GridIntervalPixel: ValueElement;
|
||||
Ntoles: ValueElement;
|
||||
SnapToGrid: ValueElement;
|
||||
Fixed: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSMidiClip {
|
||||
MidiClip: ALSMidiClipContent;
|
||||
}
|
||||
340
src/lib/exporters/ableton/types/midiTrack.ts
Normal file
@@ -0,0 +1,340 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import type { ALSTrackSendHolder } from './trackSendHolder';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Routing {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLane {
|
||||
'@Id': number;
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
LaneHeight: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLanes {
|
||||
AutomationLane: AutomationLane[];
|
||||
AreAdditionalAutomationLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipEnvelopeChooserViewState {
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
PreferModulationVisible: ValueElement;
|
||||
}
|
||||
|
||||
export interface Mixer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
Sends: ALSTrackSendHolder;
|
||||
Speaker: ManualElement;
|
||||
SoloSink: ValueElement;
|
||||
PanMode: ValueElement;
|
||||
Pan: ManualElement;
|
||||
SplitStereoPanL: ManualElement;
|
||||
SplitStereoPanR: ManualElement;
|
||||
Volume: ManualElement;
|
||||
ViewStateSessionTrackWidth: ValueElement;
|
||||
CrossFadeState: ManualElement;
|
||||
SendsListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
interface ClipSlot {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
ClipSlot: {
|
||||
Value?: MidiClip;
|
||||
};
|
||||
HasStop?: ValueElement;
|
||||
NeedRefreeze?: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipSlotList {
|
||||
ClipSlot: ClipSlot[];
|
||||
}
|
||||
|
||||
interface MidiClip {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
interface ArrangerAutomation {
|
||||
Events: {
|
||||
MidiClip: MidiClip[];
|
||||
};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
}
|
||||
|
||||
interface ClipTimeable {
|
||||
ArrangerAutomation: ArrangerAutomation;
|
||||
}
|
||||
|
||||
interface Recorder {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
}
|
||||
|
||||
interface ControllerTarget {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
}
|
||||
|
||||
interface MidiControllers {
|
||||
[key: string]: ControllerTarget;
|
||||
}
|
||||
|
||||
interface MainSequencer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: ClipSlotList;
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
ClipTimeable: ClipTimeable;
|
||||
Recorder: Recorder;
|
||||
MidiControllers: MidiControllers;
|
||||
}
|
||||
|
||||
interface FreezeSequencer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: ClipSlotList;
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
Sample: {
|
||||
ArrangerAutomation: {
|
||||
Events: {};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
VolumeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TranspositionModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TransientEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
GrainSizeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
FluxModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
SampleOffsetModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProFormantsModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
PitchViewScrollPosition: ValueElement;
|
||||
SampleOffsetModulationScrollPosition: ValueElement;
|
||||
Recorder: Recorder;
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
AutomationLanes: AutomationLanes;
|
||||
ClipEnvelopeChooserViewState: ClipEnvelopeChooserViewState;
|
||||
AudioInputRouting: Routing;
|
||||
MidiInputRouting: Routing;
|
||||
AudioOutputRouting: Routing;
|
||||
MidiOutputRouting: Routing;
|
||||
Mixer: Mixer;
|
||||
MainSequencer: MainSequencer;
|
||||
FreezeSequencer: FreezeSequencer;
|
||||
DeviceChain: {
|
||||
Devices: {
|
||||
'#': Array<any>;
|
||||
};
|
||||
SignalModulations: {};
|
||||
};
|
||||
}
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ControllerLayoutCustomization {
|
||||
PitchClassSource: ValueElement;
|
||||
OctaveSource: ValueElement;
|
||||
KeyNoteTarget: ValueElement;
|
||||
StepSize: ValueElement;
|
||||
OctaveEvery: ValueElement;
|
||||
AllowedKeys: ValueElement;
|
||||
FillerKeysMapTo: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSMidiTrackContent {
|
||||
'@Id': number;
|
||||
'@SelectedToolPanel': number;
|
||||
'@SelectedTransformationName': string;
|
||||
'@SelectedGeneratorName': string;
|
||||
'@_internalGroupId'?: string;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
};
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: {
|
||||
Envelopes: {};
|
||||
};
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
ClipSlotsListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
ArrangementClipsListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
TakeLanesListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
SavedPlayingSlot: ValueElement;
|
||||
SavedPlayingOffset: ValueElement;
|
||||
Freeze: ValueElement;
|
||||
NeedArrangerRefreeze: ValueElement;
|
||||
PostProcessFreezeClips: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
ReWireDeviceMidiTargetId: ValueElement;
|
||||
PitchbendRange: ValueElement;
|
||||
IsTuned: ValueElement;
|
||||
ControllerLayoutRemoteable: ValueElement;
|
||||
ControllerLayoutCustomization: ControllerLayoutCustomization;
|
||||
}
|
||||
|
||||
export interface ALSMidiTrack {
|
||||
MidiTrack: ALSMidiTrackContent;
|
||||
}
|
||||
372
src/lib/exporters/ableton/types/project.ts
Normal file
@@ -0,0 +1,372 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import type { ALSGroupTrack } from './groupTrack';
|
||||
import type { ALSMidiTrack } from './midiTrack';
|
||||
import type { ALSReturnTrack } from './returnTrack';
|
||||
import { ALSSceneContent } from './scene';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Routing {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLane {
|
||||
'@Id': number;
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
LaneHeight: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLanes {
|
||||
AutomationLanes: {
|
||||
AutomationLane: AutomationLane[];
|
||||
};
|
||||
AreAdditionalAutomationLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipEnvelopeChooserViewState {
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
PreferModulationVisible: ValueElement;
|
||||
}
|
||||
|
||||
interface Mixer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
Sends: {};
|
||||
Speaker: ManualElement;
|
||||
SoloSink: ValueElement;
|
||||
PanMode: ValueElement;
|
||||
Pan: ManualElement;
|
||||
SplitStereoPanL: ManualElement;
|
||||
SplitStereoPanR: ManualElement;
|
||||
Volume: ManualElement;
|
||||
ViewStateSessionTrackWidth: ValueElement;
|
||||
CrossFadeState: ManualElement;
|
||||
SendsListWrapper: LomIdElement;
|
||||
Tempo: ManualElement;
|
||||
}
|
||||
|
||||
interface DeviceChain {
|
||||
AutomationLanes: AutomationLanes;
|
||||
ClipEnvelopeChooserViewState: ClipEnvelopeChooserViewState;
|
||||
AudioInputRouting: Routing;
|
||||
MidiInputRouting: Routing;
|
||||
AudioOutputRouting: Routing;
|
||||
MidiOutputRouting: Routing;
|
||||
Mixer: Mixer;
|
||||
MainSequencer: {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: {
|
||||
ClipSlot: any[]; // Empty in template
|
||||
};
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
ClipTimeable: {
|
||||
ArrangerAutomation: {
|
||||
Events: {
|
||||
MidiClip: any[]; // Empty in template
|
||||
};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
Recorder: {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
};
|
||||
MidiControllers: {};
|
||||
};
|
||||
FreezeSequencer: {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
ClipSlotList: {
|
||||
ClipSlot: any[]; // Empty in template
|
||||
};
|
||||
MonitoringEnum: ValueElement;
|
||||
KeepRecordMonitoringLatency: ValueElement;
|
||||
Sample: {
|
||||
ArrangerAutomation: {
|
||||
Events: {};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
VolumeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TranspositionModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TransientEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
GrainSizeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
FluxModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
SampleOffsetModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProFormantsModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
ComplexProEnvelopeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
PitchViewScrollPosition: ValueElement;
|
||||
SampleOffsetModulationScrollPosition: ValueElement;
|
||||
Recorder: {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
};
|
||||
};
|
||||
DeviceChain: {
|
||||
Devices: {};
|
||||
SignalModulations: {};
|
||||
};
|
||||
}
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackDelay {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationEnvelope {
|
||||
'@Id': number;
|
||||
EnvelopeTarget: {
|
||||
PointeeId: ValueElement;
|
||||
};
|
||||
Automation: {
|
||||
Events: {
|
||||
EnumEvent: {
|
||||
'@Id': number;
|
||||
'@Time': number;
|
||||
'@Value': number;
|
||||
};
|
||||
FloatEvent: {
|
||||
'@Id': number;
|
||||
'@Time': number;
|
||||
'@Value': number;
|
||||
};
|
||||
};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface AutomationEnvelopes {
|
||||
Envelopes: {
|
||||
AutomationEnvelope: AutomationEnvelope[];
|
||||
};
|
||||
}
|
||||
|
||||
interface MasterTrack {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: TrackDelay;
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: AutomationEnvelopes;
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
ClipSlotsListWrapper: LomIdElement;
|
||||
ArrangementClipsListWrapper: LomIdElement;
|
||||
TakeLanesListWrapper: LomIdElement;
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
}
|
||||
|
||||
interface GroovePool {
|
||||
Grooves: {
|
||||
Groove: any[];
|
||||
};
|
||||
}
|
||||
|
||||
interface AutoColorPicker {
|
||||
NextColorIndex: ValueElement;
|
||||
}
|
||||
|
||||
interface VideoWindowRect {
|
||||
'@Top': number;
|
||||
'@Left': number;
|
||||
'@Bottom': number;
|
||||
'@Right': number;
|
||||
}
|
||||
|
||||
interface LiveSet {
|
||||
NextPointeeId: ValueElement;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
Tracks: {
|
||||
'#': (ALSMidiTrack | ALSGroupTrack | ALSReturnTrack)[];
|
||||
};
|
||||
MasterTrack: MasterTrack;
|
||||
GroovePool: GroovePool;
|
||||
AutomationMode: ValueElement;
|
||||
SnapAutomationToGrid: ValueElement;
|
||||
ArrangementOverdub: ValueElement;
|
||||
ColorSequenceIndex: ValueElement;
|
||||
AutoColorPickerForPlayerAndGroupTracks: AutoColorPicker;
|
||||
AutoColorPickerForReturnAndMasterTracks: AutoColorPicker;
|
||||
ViewData: ValueElement;
|
||||
ResetNonautomatedMidiControllersOnClipStarts: ValueElement;
|
||||
MidiFoldIn: ValueElement;
|
||||
MidiFoldMode: ValueElement;
|
||||
MultiClipFocusMode: ValueElement;
|
||||
MultiClipLoopBarHeight: ValueElement;
|
||||
MidiPrelisten: ValueElement;
|
||||
LinkedTrackGroups: {};
|
||||
AccidentalSpellingPreference: ValueElement;
|
||||
PreferFlatRootNote: ValueElement;
|
||||
UseWarperLegacyHiQMode: ValueElement;
|
||||
VideoWindowRect: VideoWindowRect;
|
||||
ShowVideoWindow: ValueElement;
|
||||
TrackHeaderWidth: ValueElement;
|
||||
ViewStates: {};
|
||||
Scenes: {
|
||||
Scene: ALSSceneContent[];
|
||||
};
|
||||
SendsPre: {
|
||||
SendPreBool: {
|
||||
'@Id': number;
|
||||
'@Value': string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface Ableton {
|
||||
'@MajorVersion': string;
|
||||
'@MinorVersion': string;
|
||||
'@SchemaChangeCount': string;
|
||||
'@Creator': string;
|
||||
'@Revision': string;
|
||||
LiveSet: LiveSet;
|
||||
}
|
||||
|
||||
export interface ALSProject {
|
||||
Ableton: Ableton;
|
||||
}
|
||||
224
src/lib/exporters/ableton/types/returnTrack.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
import { ALSChorus } from './effectChorus';
|
||||
import { ALSCompressor } from './effectCompressor';
|
||||
import { ALSDelay } from './effectDelay';
|
||||
import { ALSDistortion } from './effectDistortion';
|
||||
import { ALSFilter } from './effectFilter';
|
||||
import { ALSReverb } from './effectReverb';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface Routing {
|
||||
Target: ValueElement;
|
||||
UpperDisplayString: ValueElement;
|
||||
LowerDisplayString: ValueElement;
|
||||
MpeSettings: {
|
||||
ZoneType: ValueElement;
|
||||
FirstNoteChannel: ValueElement;
|
||||
LastNoteChannel: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
interface AutomationLane {
|
||||
'@Id': number;
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
LaneHeight: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationLanes {
|
||||
AutomationLane: AutomationLane[];
|
||||
AreAdditionalAutomationLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
interface ClipEnvelopeChooserViewState {
|
||||
SelectedDevice: ValueElement;
|
||||
SelectedEnvelope: ValueElement;
|
||||
PreferModulationVisible: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackSendHolder {
|
||||
'@Id': number;
|
||||
Send: ManualElement;
|
||||
Active: ValueElement;
|
||||
}
|
||||
|
||||
interface Sends {
|
||||
TrackSendHolder: TrackSendHolder[];
|
||||
}
|
||||
|
||||
interface Mixer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
Sends: Sends;
|
||||
Speaker: ManualElement;
|
||||
SoloSink: ValueElement;
|
||||
PanMode: ValueElement;
|
||||
Pan: ManualElement;
|
||||
SplitStereoPanL: ManualElement;
|
||||
SplitStereoPanR: ManualElement;
|
||||
Volume: ManualElement;
|
||||
ViewStateSesstionTrackWidth: ValueElement;
|
||||
CrossFadeState: ManualElement;
|
||||
SendsListWrapper: LomIdElement;
|
||||
}
|
||||
|
||||
interface Sample {
|
||||
ArrangerAutomation: {
|
||||
Events: {};
|
||||
AutomationTransformViewState: {
|
||||
IsTransformPending: ValueElement;
|
||||
TimeAndValueTransforms: {};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface FreezeSequencer {
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: LomIdElement;
|
||||
Pointee: {
|
||||
'@Id': number;
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: {
|
||||
Value: {};
|
||||
};
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: {
|
||||
Value: {};
|
||||
};
|
||||
ClipSlotList: {};
|
||||
MonitoringEnum: ValueElement;
|
||||
Sample: Sample;
|
||||
VolumeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
TranspositionModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
GrainSizeModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
FluxModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
SampleOffsetModulationTarget: {
|
||||
'@Id': number;
|
||||
LockEnvelope: ValueElement;
|
||||
};
|
||||
PitchViewScrollPosition: ValueElement;
|
||||
SampleOffsetModulationScrollPosition: ValueElement;
|
||||
Recorder: {
|
||||
IsArmed: ValueElement;
|
||||
TakeCounter: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
interface Name {
|
||||
EffectiveName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
MemorizedFirstClipName: ValueElement;
|
||||
}
|
||||
|
||||
interface TrackDelay {
|
||||
Value: ValueElement;
|
||||
IsValueSampleBased: ValueElement;
|
||||
}
|
||||
|
||||
interface AutomationEnvelopes {
|
||||
Envelopes: {};
|
||||
}
|
||||
|
||||
interface TakeLanes {
|
||||
TakeLanes: {};
|
||||
AreTakeLanesFolded: ValueElement;
|
||||
}
|
||||
|
||||
type EffectDevice = ALSChorus | ALSCompressor | ALSDelay | ALSDistortion | ALSFilter | ALSReverb;
|
||||
|
||||
interface DeviceChain {
|
||||
AutomationLanes: AutomationLanes;
|
||||
ClipEnvelopeChooserViewState: ClipEnvelopeChooserViewState;
|
||||
AudioInputRouting: Routing;
|
||||
MidiInputRouting: Routing;
|
||||
AudioOutputRouting: Routing;
|
||||
MidiOutputRouting: Routing;
|
||||
Mixer: Mixer;
|
||||
DeviceChain: {
|
||||
Devices: EffectDevice;
|
||||
SignalModulations: {};
|
||||
};
|
||||
FreezeSequencer: FreezeSequencer;
|
||||
}
|
||||
|
||||
export interface ALSReturnTrackContent {
|
||||
'@Id': number;
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsContentSelectedInDocument: ValueElement;
|
||||
PreferredContentViewMode: ValueElement;
|
||||
TrackDelay: TrackDelay;
|
||||
Name: Name;
|
||||
Color: ValueElement;
|
||||
AutomationEnvelopes: AutomationEnvelopes;
|
||||
TrackGroupId: ValueElement;
|
||||
TrackUnfolded: ValueElement;
|
||||
DevicesListWrapper: LomIdElement;
|
||||
ClipSlotsListWrapper: LomIdElement;
|
||||
ViewData: ValueElement;
|
||||
TakeLanes: TakeLanes;
|
||||
LinkedTrackGroupId: ValueElement;
|
||||
DeviceChain: DeviceChain;
|
||||
}
|
||||
|
||||
export interface ALSReturnTrack {
|
||||
ReturnTrack: ALSReturnTrackContent;
|
||||
}
|
||||
34
src/lib/exporters/ableton/types/scene.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { ValueElement } from './common';
|
||||
|
||||
interface FollowAction {
|
||||
FollowTime: ValueElement;
|
||||
IsLinked: ValueElement;
|
||||
LoopIterations: ValueElement;
|
||||
FollowActionA: ValueElement;
|
||||
FollowActionB: ValueElement;
|
||||
FollowChanceA: ValueElement;
|
||||
FollowChanceB: ValueElement;
|
||||
JumpIndexA: ValueElement;
|
||||
JumpIndexB: ValueElement;
|
||||
FollowActionEnabled: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSSceneContent {
|
||||
'@Id': number;
|
||||
FollowAction: FollowAction;
|
||||
Name: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
Color: ValueElement;
|
||||
Tempo: ValueElement;
|
||||
IsTempoEnabled: ValueElement;
|
||||
TimeSignatureId: ValueElement;
|
||||
IsTimeSignatureEnabled: ValueElement;
|
||||
LomId: ValueElement;
|
||||
ClipSlotsListWrapper: {
|
||||
'@LomId': number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSScene {
|
||||
Scene: ALSSceneContent;
|
||||
}
|
||||
545
src/lib/exporters/ableton/types/simpler.ts
Normal file
@@ -0,0 +1,545 @@
|
||||
import {
|
||||
AutomationTarget,
|
||||
FileRef,
|
||||
LomIdElement,
|
||||
MidiCCOnOffThresholds,
|
||||
MidiControllerRange,
|
||||
ModulationTarget,
|
||||
PresetRef,
|
||||
ValueElement,
|
||||
} from './common';
|
||||
|
||||
interface ManualElement extends LomIdElement {
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange?: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget?: ModulationTarget;
|
||||
MidiCCOnOffThresholds?: MidiCCOnOffThresholds;
|
||||
}
|
||||
|
||||
interface BranchSourceContext {
|
||||
_attrs: { Id: number };
|
||||
OriginalFileRef: {};
|
||||
BrowserContentPath: ValueElement;
|
||||
LocalFiltersJson: ValueElement;
|
||||
PresetRef: PresetRef;
|
||||
BranchDeviceId: ValueElement;
|
||||
}
|
||||
|
||||
interface SourceContext {
|
||||
Value: BranchSourceContext;
|
||||
}
|
||||
|
||||
interface LastPresetRef {
|
||||
Value: any;
|
||||
}
|
||||
|
||||
interface WarpMarker {
|
||||
'@Id': number;
|
||||
'@SecTime': number;
|
||||
'@BeatTime': number;
|
||||
}
|
||||
|
||||
interface WarpMarkers {
|
||||
WarpMarker: WarpMarker[];
|
||||
}
|
||||
|
||||
interface TimeSignature {
|
||||
TimeSignatures: {
|
||||
RemoteableTimeSignature: {
|
||||
_attrs: { Id: number };
|
||||
Numerator: ValueElement;
|
||||
Denominator: ValueElement;
|
||||
Time: ValueElement;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface BeatGrid {
|
||||
FixedNumerator: ValueElement;
|
||||
FixedDenominator: ValueElement;
|
||||
GridIntervalPixel: ValueElement;
|
||||
Ntoles: ValueElement;
|
||||
SnapToGrid: ValueElement;
|
||||
Fixed: ValueElement;
|
||||
}
|
||||
|
||||
interface SampleWarpProperties {
|
||||
WarpMarkers: WarpMarkers;
|
||||
WarpMode: ValueElement;
|
||||
GranularityTones: ValueElement;
|
||||
GranularityTexture: ValueElement;
|
||||
FluctuationTexture: ValueElement;
|
||||
ComplexProFormants: ValueElement;
|
||||
ComplexProEnvelope: ValueElement;
|
||||
TransientResolution: ValueElement;
|
||||
TransientLoopMode: ValueElement;
|
||||
TransientEnvelope: ValueElement;
|
||||
IsWarped: ValueElement;
|
||||
Onsets: {
|
||||
UserOnsets: {};
|
||||
HasUserOnsets: ValueElement;
|
||||
};
|
||||
TimeSignature: TimeSignature;
|
||||
BeatGrid: BeatGrid;
|
||||
}
|
||||
|
||||
interface SampleRef {
|
||||
FileRef: FileRef;
|
||||
LastModDate: ValueElement;
|
||||
SourceContext: {};
|
||||
SampleUsageHint: ValueElement;
|
||||
DefaultDuration: ValueElement;
|
||||
DefaultSampleRate: ValueElement;
|
||||
SamplesToAutoWarp: ValueElement;
|
||||
}
|
||||
|
||||
interface SustainLoop {
|
||||
Start: ValueElement;
|
||||
End: ValueElement;
|
||||
Mode: ValueElement;
|
||||
Crossfade: ValueElement;
|
||||
Detune: ValueElement;
|
||||
}
|
||||
|
||||
interface ReleaseLoop {
|
||||
Start: ValueElement;
|
||||
End: ValueElement;
|
||||
Mode: ValueElement;
|
||||
Crossfade: ValueElement;
|
||||
Detune: ValueElement;
|
||||
}
|
||||
|
||||
interface MultiSamplePart {
|
||||
_attrs: {
|
||||
Id: number;
|
||||
InitUpdateAreSlicesFromOnsetsEditableAfterRead: boolean;
|
||||
HasImportedSlicePoints: boolean;
|
||||
NeedsAnalysisData: boolean;
|
||||
};
|
||||
LomId: ValueElement;
|
||||
Name: ValueElement;
|
||||
Selection: ValueElement;
|
||||
IsActive: ValueElement;
|
||||
Solo: ValueElement;
|
||||
KeyRange: {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
};
|
||||
VelocityRange: {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
};
|
||||
SelectorRange: {
|
||||
Min: ValueElement;
|
||||
Max: ValueElement;
|
||||
CrossfadeMin: ValueElement;
|
||||
CrossfadeMax: ValueElement;
|
||||
};
|
||||
RootKey: ValueElement;
|
||||
Detune: ValueElement;
|
||||
TuneScale: ValueElement;
|
||||
Panorama: ValueElement;
|
||||
Volume: ValueElement;
|
||||
Link: ValueElement;
|
||||
SampleStart: ValueElement;
|
||||
SampleEnd: ValueElement;
|
||||
SustainLoop: SustainLoop;
|
||||
ReleaseLoop: ReleaseLoop;
|
||||
SampleRef: SampleRef;
|
||||
SlicingThreshold: ValueElement;
|
||||
SlicingBeatGrid: ValueElement;
|
||||
SlicingRegions: ValueElement;
|
||||
SlicingStyle: ValueElement;
|
||||
SampleWarpProperties: SampleWarpProperties;
|
||||
InitialSlicePointsFromOnsets: {
|
||||
SlicePoint: {
|
||||
TimeInSeconds: number;
|
||||
Rank: number;
|
||||
NormalizedEnergy: number;
|
||||
};
|
||||
};
|
||||
SlicePoints: {};
|
||||
ManualSlicePoints: {};
|
||||
BeatSlicePoints: {};
|
||||
RegionSlicePoints: {};
|
||||
UseDynamicBeatSlices: ValueElement;
|
||||
UseDynamicRegionSlices: ValueElement;
|
||||
AreSlicesFromOnsetsEditable: ValueElement;
|
||||
}
|
||||
|
||||
interface SampleParts {
|
||||
MultiSamplePart: MultiSamplePart;
|
||||
}
|
||||
|
||||
interface MultiSampleMap {
|
||||
SampleParts: SampleParts;
|
||||
LoadInRam: ValueElement;
|
||||
LayerCrossfade: ValueElement;
|
||||
SourceContext: {};
|
||||
RoundRobin: ValueElement;
|
||||
RoundRobinMode: ValueElement;
|
||||
RoundRobinResetPeriod: ValueElement;
|
||||
RoundRobinRandomSeed: ValueElement;
|
||||
}
|
||||
|
||||
interface LoopModulator {
|
||||
IsModulated: ValueElement;
|
||||
SampleStart: ManualElement;
|
||||
SampleLength: ManualElement;
|
||||
LoopOn: ManualElement;
|
||||
LoopLength: ManualElement;
|
||||
LoopFade: ManualElement;
|
||||
}
|
||||
|
||||
interface LoopModulators {
|
||||
LoopModulator: LoopModulator;
|
||||
}
|
||||
|
||||
interface Player {
|
||||
MultiSampleMap: MultiSampleMap;
|
||||
LoopModulators: LoopModulators;
|
||||
Reverse: ManualElement;
|
||||
Snap: ManualElement;
|
||||
SampleSelector: ManualElement;
|
||||
SubOsc: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
InterpolationMode: ValueElement;
|
||||
UseConstPowCrossfade: ValueElement;
|
||||
}
|
||||
|
||||
interface SimplerPitchEnvelope {
|
||||
_attrs: { Id: number };
|
||||
AttackTime: ManualElement;
|
||||
AttackLevel: ManualElement;
|
||||
AttackSlope: ManualElement;
|
||||
DecayTime: ManualElement;
|
||||
DecayLevel: ManualElement;
|
||||
DecaySlope: ManualElement;
|
||||
SustainLevel: ManualElement;
|
||||
ReleaseTime: ManualElement;
|
||||
ReleaseLevel: ManualElement;
|
||||
ReleaseSlope: ManualElement;
|
||||
LoopMode: ManualElement;
|
||||
LoopTime: ManualElement;
|
||||
RepeatTime: ManualElement;
|
||||
TimeVelScale: ManualElement;
|
||||
CurrentOverlay: ValueElement;
|
||||
Amount: ManualElement;
|
||||
ScrollPosition: ValueElement;
|
||||
}
|
||||
|
||||
interface Pitch {
|
||||
TransposeKey: ManualElement;
|
||||
TransposeFine: ManualElement;
|
||||
PitchLfoAmount: ManualElement;
|
||||
Envelope: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: SimplerPitchEnvelope;
|
||||
};
|
||||
};
|
||||
ScrollPosition: ValueElement;
|
||||
}
|
||||
|
||||
interface SimplerFilter {
|
||||
_attrs: { Id: number };
|
||||
LegacyType: ManualElement;
|
||||
Type: ManualElement;
|
||||
CircuitLpHp: ManualElement;
|
||||
CircuitBpNoMo: ManualElement;
|
||||
Slope: ManualElement;
|
||||
Freq: ManualElement;
|
||||
LegacyQ: ManualElement;
|
||||
Res: ManualElement;
|
||||
X: ManualElement;
|
||||
Drive: ManualElement;
|
||||
Envelope: {
|
||||
AttackTime: ManualElement;
|
||||
AttackLevel: ManualElement;
|
||||
AttackSlope: ManualElement;
|
||||
DecayTime: ManualElement;
|
||||
DecayLevel: ManualElement;
|
||||
DecaySlope: ManualElement;
|
||||
SustainLevel: ManualElement;
|
||||
ReleaseTime: ManualElement;
|
||||
ReleaseLevel: ManualElement;
|
||||
ReleaseSlope: ManualElement;
|
||||
LoopMode: ManualElement;
|
||||
LoopTime: ManualElement;
|
||||
RepeatTime: ManualElement;
|
||||
TimeVelScale: ManualElement;
|
||||
CurrentOverlay: ValueElement;
|
||||
IsOn: ManualElement;
|
||||
Amount: ManualElement;
|
||||
ScrollPosition: ValueElement;
|
||||
};
|
||||
ModByPitch: ManualElement;
|
||||
ModByVelocity: ManualElement;
|
||||
ModByLfo: ManualElement;
|
||||
}
|
||||
|
||||
interface Filter {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {
|
||||
SimplerFilter: SimplerFilter;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface VolumeAndPan {
|
||||
Volume: ManualElement;
|
||||
VolumeVelScale: ManualElement;
|
||||
VolumeKeyScale: ManualElement;
|
||||
VolumeLfoAmount: ManualElement;
|
||||
Panorama: ManualElement;
|
||||
PanoramaKeyScale: ManualElement;
|
||||
PanoramaRnd: ManualElement;
|
||||
PanoramaLfoAmount: ManualElement;
|
||||
Envelope: {
|
||||
AttackTime: ManualElement;
|
||||
AttackLevel: ManualElement;
|
||||
AttackSlope: ManualElement;
|
||||
DecayTime: ManualElement;
|
||||
DecayLevel: ManualElement;
|
||||
DecaySlope: ManualElement;
|
||||
SustainLevel: ManualElement;
|
||||
ReleaseTime: ManualElement;
|
||||
ReleaseLevel: ManualElement;
|
||||
ReleaseSlope: ManualElement;
|
||||
LoopMode: ManualElement;
|
||||
LoopTime: ManualElement;
|
||||
RepeatTime: ManualElement;
|
||||
TimeVelScale: ManualElement;
|
||||
CurrentOverlay: ValueElement;
|
||||
};
|
||||
OneShotEnvelope: {
|
||||
FadeInTime: ManualElement;
|
||||
SustainMode: ManualElement;
|
||||
FadeOutTime: ManualElement;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSOriginalSimplerContent {
|
||||
_attrs: { Id: number };
|
||||
LomId: ValueElement;
|
||||
LomIdView: ValueElement;
|
||||
IsExpanded: ValueElement;
|
||||
BreakoutIsExpanded: ValueElement;
|
||||
On: ManualElement;
|
||||
ModulationSourceCount: ValueElement;
|
||||
ParametersListWrapper: {
|
||||
LomId: ValueElement;
|
||||
};
|
||||
Pointee: {
|
||||
_attrs: { Id: number };
|
||||
};
|
||||
LastSelectedTimeableIndex: ValueElement;
|
||||
LastSelectedClipEnvelopeIndex: ValueElement;
|
||||
LastPresetRef: LastPresetRef;
|
||||
LockedScripts: {};
|
||||
IsFolded: ValueElement;
|
||||
ShouldShowPresetName: ValueElement;
|
||||
UserName: ValueElement;
|
||||
Annotation: ValueElement;
|
||||
SourceContext: SourceContext;
|
||||
MpePitchBendUsesTuning: ValueElement;
|
||||
OverwriteProtectionNumber: ValueElement;
|
||||
Player: Player;
|
||||
Pitch: Pitch;
|
||||
Filter: Filter;
|
||||
Shaper: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
VolumeAndPan: VolumeAndPan;
|
||||
AuxEnv: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
Lfo: {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {
|
||||
SimplerLfo: {
|
||||
_attrs: { Id: number };
|
||||
Type: ManualElement;
|
||||
Frequency: ManualElement;
|
||||
RateType: ManualElement;
|
||||
BeatRate: ManualElement;
|
||||
StereoMode: ManualElement;
|
||||
Spin: ManualElement;
|
||||
Phase: ManualElement;
|
||||
Offset: ManualElement;
|
||||
FrequencyKeyScale: ManualElement;
|
||||
Smooth: ManualElement;
|
||||
Attack: ManualElement;
|
||||
Retrigger: ManualElement;
|
||||
Width: ManualElement;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
AuxLfos: {
|
||||
'0': {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
'1': {
|
||||
IsOn: ManualElement;
|
||||
Slot: {
|
||||
Value: {};
|
||||
};
|
||||
};
|
||||
};
|
||||
KeyDst: {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
};
|
||||
VelDst: {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
};
|
||||
RelVelDst: {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
};
|
||||
MidiCtrl: {
|
||||
'0': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'1': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'2': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'3': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'4': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
'5': {
|
||||
'ModConnections.0': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
'ModConnections.1': {
|
||||
Amount: ValueElement;
|
||||
Connection: ValueElement;
|
||||
};
|
||||
Feedback: ValueElement;
|
||||
};
|
||||
};
|
||||
Globals: {
|
||||
NumVoices: ValueElement;
|
||||
NumVoicesEnvTimeControl: ValueElement;
|
||||
RetriggerMode: ValueElement;
|
||||
ModulationResolution: ValueElement;
|
||||
SpreadAmount: ManualElement;
|
||||
KeyZoneShift: ManualElement;
|
||||
PortamentoMode: ManualElement;
|
||||
PortamentoTime: ManualElement;
|
||||
PitchBendRange: ValueElement;
|
||||
MpePitchBendRange: ValueElement;
|
||||
ScrollPosition: ValueElement;
|
||||
EnvScale: {
|
||||
EnvTime: ManualElement;
|
||||
EnvTimeKeyScale: ManualElement;
|
||||
EnvTimeIncludeAttack: ManualElement;
|
||||
};
|
||||
IsSimpler: ValueElement;
|
||||
PlaybackMode: ValueElement;
|
||||
LegacyMode: ValueElement;
|
||||
};
|
||||
ViewSettings: {
|
||||
SelectedPage: ValueElement;
|
||||
ZoneEditorVisible: ValueElement;
|
||||
Seconds: ValueElement;
|
||||
SelectedSampleChannel: ValueElement;
|
||||
VerticalSampleZoom: ValueElement;
|
||||
IsAutoSelectEnabled: ValueElement;
|
||||
SimplerBreakoutVisible: ValueElement;
|
||||
};
|
||||
SimplerSlicing: {
|
||||
PlaybackMode: ValueElement;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ALSSimpler {
|
||||
OriginalSimpler: ALSOriginalSimplerContent;
|
||||
}
|
||||
19
src/lib/exporters/ableton/types/trackSendHolder.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { AutomationTarget, MidiControllerRange, ModulationTarget, ValueElement } from './common';
|
||||
|
||||
interface Send {
|
||||
LomId: ValueElement;
|
||||
Manual: ValueElement;
|
||||
MidiControllerRange: MidiControllerRange;
|
||||
AutomationTarget: AutomationTarget;
|
||||
ModulationTarget: ModulationTarget;
|
||||
}
|
||||
|
||||
export interface ALSTrackSendHolderContent {
|
||||
'@Id': number;
|
||||
Send: Send;
|
||||
Active: ValueElement;
|
||||
}
|
||||
|
||||
export interface ALSTrackSendHolder {
|
||||
TrackSendHolder: ALSTrackSendHolderContent;
|
||||
}
|
||||
138
src/lib/exporters/ableton/utils.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { create } from 'xmlbuilder2';
|
||||
|
||||
export const TIME_SIGNATURES: { [key: string]: number } = {
|
||||
'1/4': 196,
|
||||
'2/4': 198,
|
||||
'3/4': 200,
|
||||
'4/4': 201,
|
||||
'5/4': 202,
|
||||
'6/4': 203,
|
||||
'7/4': 204,
|
||||
'8/4': 205,
|
||||
'5/8': 206,
|
||||
'6/8': 207,
|
||||
'7/8': 208,
|
||||
'9/8': 209,
|
||||
'12/8': 210,
|
||||
'3/2': 211,
|
||||
'4/2': 212,
|
||||
'5/2': 213,
|
||||
'6/2': 214,
|
||||
'7/2': 215,
|
||||
'1/2': 216,
|
||||
'1/1': 217,
|
||||
'2/1': 218,
|
||||
'3/1': 219,
|
||||
'4/1': 220,
|
||||
'5/1': 221,
|
||||
'3/8': 299,
|
||||
};
|
||||
const MIN_ID = 22000;
|
||||
const START_ID = 22000;
|
||||
|
||||
let _id = START_ID;
|
||||
const templateCache: Record<string, any> = {};
|
||||
|
||||
const templateMap: Record<string, () => Promise<any>> = {
|
||||
midiClip: () => import('./templates/midiClip.xml?raw'),
|
||||
midiTrack: () => import('./templates/midiTrack.xml?raw'),
|
||||
project: () => import('./templates/project.xml?raw'),
|
||||
simpler: () => import('./templates/simpler.xml?raw'),
|
||||
scene: () => import('./templates/scene.xml?raw'),
|
||||
groupTrack: () => import('./templates/groupTrack.xml?raw'),
|
||||
drumRack: () => import('./templates/drumRack.xml?raw'),
|
||||
drumBranch: () => import('./templates/drumBranch.xml?raw'),
|
||||
returnTrack: () => import('./templates/returnTrack.xml?raw'),
|
||||
trackSendHolder: () => import('./templates/trackSendHolder.xml?raw'),
|
||||
effectReverb: () => import('./templates/effectReverb.xml?raw'),
|
||||
effectDelay: () => import('./templates/effectDelay.xml?raw'),
|
||||
effectChorus: () => import('./templates/effectChorus.xml?raw'),
|
||||
effectDistortion: () => import('./templates/effectDistortion.xml?raw'),
|
||||
effectFilter: () => import('./templates/effectFilter.xml?raw'),
|
||||
effectCompressor: () => import('./templates/effectCompressor.xml?raw'),
|
||||
};
|
||||
|
||||
export async function loadTemplate<T>(templateName: string): Promise<T> {
|
||||
if (templateCache[templateName]) {
|
||||
return templateCache[templateName];
|
||||
}
|
||||
|
||||
const importFn = templateMap[templateName];
|
||||
if (!importFn) {
|
||||
throw new Error(`Unknown template: ${templateName}`);
|
||||
}
|
||||
|
||||
const templateModule = await importFn();
|
||||
const parsed = create(templateModule.default).toObject();
|
||||
templateCache[templateName] = parsed;
|
||||
return parsed as T;
|
||||
}
|
||||
|
||||
export function koEnvRangeToSeconds(value: number, maxSeconds: number) {
|
||||
if (value < 0 || value > 255) {
|
||||
throw new RangeError('Value must be between 0 and 255');
|
||||
}
|
||||
return (value / 255) * maxSeconds;
|
||||
}
|
||||
|
||||
export function fixIds(node: any): any {
|
||||
if (Array.isArray(node)) {
|
||||
return node.map((n) => fixIds(n));
|
||||
}
|
||||
|
||||
if (node?.['@Id']) {
|
||||
const idNum = parseInt(String(node['@Id']), 10);
|
||||
|
||||
if (idNum > MIN_ID) {
|
||||
node['@Id'] = _id;
|
||||
|
||||
_id++;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof node === 'object') {
|
||||
Object.keys(node).forEach((key) => {
|
||||
if (!key.startsWith('@')) {
|
||||
node[key] = fixIds(node[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
export function getId() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
export async function gzipString(content: string) {
|
||||
const encoder = new TextEncoder();
|
||||
const input = encoder.encode(content);
|
||||
|
||||
const cs = new CompressionStream('gzip');
|
||||
const writer = cs.writable.getWriter();
|
||||
|
||||
writer.write(input);
|
||||
writer.close();
|
||||
|
||||
const compressed = await new Response(cs.readable).arrayBuffer();
|
||||
|
||||
return compressed;
|
||||
}
|
||||
|
||||
export function filterFreqFromNormalized(x: number) {
|
||||
const fMin = 30;
|
||||
const fMax = 22000;
|
||||
|
||||
return fMin * (fMax / fMin) ** x;
|
||||
}
|
||||
|
||||
export function toNativePath(posixPath: string): string {
|
||||
const isWindows = navigator.userAgent.includes('indows');
|
||||
const pathSep = isWindows ? '\\' : '/';
|
||||
if (pathSep === '/') {
|
||||
return posixPath;
|
||||
}
|
||||
|
||||
return posixPath.split('/').join(pathSep);
|
||||
}
|
||||
482
src/lib/exporters/dawProject.ts
Normal file
@@ -0,0 +1,482 @@
|
||||
import { toXML } from 'jstoxml';
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportStatus,
|
||||
Note,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../types/types';
|
||||
import dawProjectTransformer, {
|
||||
DawClip,
|
||||
DawClipSlot,
|
||||
DawLane,
|
||||
DawScene,
|
||||
DawTrack,
|
||||
} from '../transformers/dawProject';
|
||||
import { AbortError } from '../utils';
|
||||
import { collectSamples, getNextColor, getQuarterNotesPerBar } from './utils';
|
||||
|
||||
const PROJECT_NAME = 'EP-133: Export To DAW';
|
||||
|
||||
const XML_CONFIG = {
|
||||
indent: ' ',
|
||||
header: true,
|
||||
};
|
||||
|
||||
let _id = 0;
|
||||
|
||||
function genId() {
|
||||
return `id${_id++}`;
|
||||
}
|
||||
|
||||
function buildMasterTrack() {
|
||||
return {
|
||||
_name: 'Track',
|
||||
_attrs: {
|
||||
name: 'Master',
|
||||
id: genId(),
|
||||
loaded: 'true',
|
||||
contentType: 'audio notes',
|
||||
},
|
||||
_content: {
|
||||
_name: 'Channel',
|
||||
_attrs: {
|
||||
id: '__MASTER__',
|
||||
role: 'master',
|
||||
solo: 'false',
|
||||
audioChannels: '2',
|
||||
},
|
||||
_content: [
|
||||
{
|
||||
_name: 'Mute',
|
||||
_attrs: {
|
||||
name: 'Mute',
|
||||
value: 'false',
|
||||
id: genId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Pan',
|
||||
_attrs: {
|
||||
name: 'Pan',
|
||||
id: genId(),
|
||||
max: '1.000000',
|
||||
min: '0.000000',
|
||||
unit: 'normalized',
|
||||
value: '0.500000',
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Volume',
|
||||
_attrs: {
|
||||
name: 'Volume',
|
||||
id: genId(),
|
||||
max: '2.000000',
|
||||
min: '0.000000',
|
||||
unit: 'linear',
|
||||
value: '1.000000',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildTrack(track: DawTrack) {
|
||||
return {
|
||||
_name: 'Track',
|
||||
_attrs: {
|
||||
name: track.soundId ? `${String(track.soundId).padStart(3, '0')} ${track.name}` : track.name,
|
||||
id: `__TRACK_${track.padCode}__`,
|
||||
loaded: 'true',
|
||||
contentType: 'notes',
|
||||
color: getNextColor(),
|
||||
},
|
||||
_content: {
|
||||
_name: 'Channel',
|
||||
_attrs: {
|
||||
audioChannels: '2',
|
||||
destination: '__MASTER__',
|
||||
role: 'regular',
|
||||
solo: 'false',
|
||||
id: genId(),
|
||||
},
|
||||
_content: [
|
||||
{
|
||||
_name: 'Devices',
|
||||
_content: {
|
||||
_name: 'BuiltinDevice',
|
||||
_attrs: {
|
||||
deviceName: 'Sampler',
|
||||
deviceRole: 'instrument',
|
||||
loaded: 'true',
|
||||
id: genId(),
|
||||
name: track.name,
|
||||
},
|
||||
_content: [
|
||||
{
|
||||
_name: 'Parameters',
|
||||
_attrs: {},
|
||||
},
|
||||
{
|
||||
_name: 'Enabled',
|
||||
_attrs: {
|
||||
value: 'true',
|
||||
id: genId(),
|
||||
name: 'On/Off',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Mute',
|
||||
_attrs: {
|
||||
name: 'Mute',
|
||||
value: 'false',
|
||||
id: genId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Pan',
|
||||
_attrs: {
|
||||
name: 'Pan',
|
||||
id: genId(),
|
||||
max: '1.000000',
|
||||
min: '0.000000',
|
||||
unit: 'normalized',
|
||||
value: '0.500000',
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'Volume',
|
||||
_attrs: {
|
||||
name: 'Volume',
|
||||
id: genId(),
|
||||
max: '2.000000',
|
||||
min: '0.000000',
|
||||
unit: 'linear',
|
||||
value: `${track.volume}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildStructure(tracks: DawTrack[]) {
|
||||
return {
|
||||
_name: 'Structure',
|
||||
_content: [buildMasterTrack(), ...tracks.map((t) => buildTrack(t))],
|
||||
};
|
||||
}
|
||||
|
||||
function buildNote(note: Note, index: number, notes: Note[]) {
|
||||
let dur = note.duration / 96;
|
||||
const nextNote = notes[index + 1];
|
||||
|
||||
// making sure same notes are not overlapping
|
||||
if (
|
||||
nextNote &&
|
||||
nextNote.note === note.note &&
|
||||
note.position / 96 + dur > nextNote.position / 96
|
||||
) {
|
||||
dur = nextNote.position / 96 - note.position / 96;
|
||||
}
|
||||
|
||||
return {
|
||||
_name: 'Note',
|
||||
_attrs: {
|
||||
time: note.position / 96,
|
||||
duration: dur,
|
||||
channel: 0,
|
||||
key: note.note,
|
||||
vel: note.velocity / 127,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildClip(clip: DawClip) {
|
||||
const barLength = getQuarterNotesPerBar(
|
||||
clip.sceneTimeSignature.numerator,
|
||||
clip.sceneTimeSignature.denominator,
|
||||
);
|
||||
|
||||
return {
|
||||
_name: 'Clip',
|
||||
_attrs: {
|
||||
time: clip.offset * barLength,
|
||||
duration: clip.sceneBars * barLength,
|
||||
playStart: 0,
|
||||
loopStart: 0,
|
||||
loopEnd: clip.bars * barLength,
|
||||
enable: 'true',
|
||||
},
|
||||
_content: {
|
||||
_name: 'Notes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: clip.notes.map(buildNote),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildLane(lane: DawLane) {
|
||||
return {
|
||||
_name: 'Lanes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
track: `__TRACK_${lane.padCode}__`,
|
||||
},
|
||||
_content: {
|
||||
_name: 'Clips',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: lane.clips.map((clip) => buildClip(clip)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildArrangement(lanes: DawLane[]) {
|
||||
return {
|
||||
_name: 'Arrangement',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: {
|
||||
_name: 'Lanes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
timeUnit: 'beats',
|
||||
},
|
||||
_content: lanes.map((lane) => buildLane(lane)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildSceneClip(clip: DawClip) {
|
||||
const beatsInBar = clip.sceneTimeSignature.numerator;
|
||||
|
||||
return {
|
||||
_name: 'Clip',
|
||||
_attrs: {
|
||||
time: clip.offset * beatsInBar,
|
||||
duration: clip.bars * beatsInBar,
|
||||
playStart: 0,
|
||||
loopStart: 0,
|
||||
loopEnd: clip.bars * beatsInBar,
|
||||
enable: 'true',
|
||||
},
|
||||
_content: {
|
||||
_name: 'Notes',
|
||||
_attrs: {
|
||||
id: genId(),
|
||||
},
|
||||
_content: clip.notes.map(buildNote),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildClipSlot(clipSlot: DawClipSlot) {
|
||||
return {
|
||||
_name: 'ClipSlot',
|
||||
_attrs: {
|
||||
track: `__TRACK_${clipSlot.track.padCode}__`,
|
||||
id: genId(),
|
||||
hasStop: 'true',
|
||||
},
|
||||
_content: clipSlot.clip.map((clip) => buildSceneClip(clip)),
|
||||
};
|
||||
}
|
||||
|
||||
function buildScene(scene: DawScene) {
|
||||
return {
|
||||
_name: 'Scene',
|
||||
_attrs: {
|
||||
name: scene.name,
|
||||
id: `__SCENE_${scene.name}__`,
|
||||
},
|
||||
_content: {
|
||||
_name: 'Lanes',
|
||||
_content: scene.clipSlot.map((clipSlot) => buildClipSlot(clipSlot)),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildScenes(scenes: DawScene[]) {
|
||||
return {
|
||||
_name: 'Scenes',
|
||||
_content: scenes.map((scene) => buildScene(scene)),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildMetadataXml() {
|
||||
const xml = toXML(
|
||||
{
|
||||
MetaData: {
|
||||
Title: '',
|
||||
Artist: '',
|
||||
Album: '',
|
||||
OriginalArtist: '',
|
||||
Songwriter: '',
|
||||
Producer: '',
|
||||
Year: '',
|
||||
Genre: '',
|
||||
Copyright: '',
|
||||
Comment: `Made with ${PROJECT_NAME}`,
|
||||
},
|
||||
},
|
||||
XML_CONFIG,
|
||||
);
|
||||
|
||||
return new Blob([xml], { type: 'text/xml' });
|
||||
}
|
||||
|
||||
export async function buildProjectXml(projectData: ProjectRawData, exporterParams: ExporterParams) {
|
||||
const transformedData = dawProjectTransformer(projectData, exporterParams);
|
||||
const { timeSignature } = projectData.scenesSettings;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log('projectData', projectData);
|
||||
console.log('transformedData', transformedData);
|
||||
}
|
||||
|
||||
_id = 0;
|
||||
|
||||
const application = {
|
||||
_name: 'Application',
|
||||
_attrs: {
|
||||
name: PROJECT_NAME,
|
||||
version: '1.0',
|
||||
},
|
||||
};
|
||||
|
||||
const transport = {
|
||||
_name: 'Transport',
|
||||
_content: [
|
||||
{
|
||||
_name: 'Tempo',
|
||||
_attrs: {
|
||||
unit: 'bpm',
|
||||
value: `${projectData.settings.bpm}`,
|
||||
name: 'Tempo',
|
||||
id: genId(),
|
||||
},
|
||||
},
|
||||
{
|
||||
_name: 'TimeSignature',
|
||||
_attrs: {
|
||||
numerator: timeSignature.numerator,
|
||||
denominator: timeSignature.denominator,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const main = toXML(
|
||||
{
|
||||
_name: 'Project',
|
||||
_attrs: {
|
||||
version: '1.0',
|
||||
},
|
||||
_content: [
|
||||
application,
|
||||
transport,
|
||||
buildStructure(transformedData.tracks),
|
||||
buildArrangement(transformedData.lanes),
|
||||
exporterParams.clips ? buildScenes(transformedData.scenes) : false,
|
||||
].filter(Boolean),
|
||||
},
|
||||
XML_CONFIG,
|
||||
);
|
||||
|
||||
return new Blob([main], { type: 'text/xml' });
|
||||
}
|
||||
|
||||
async function exportDawProject(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
) {
|
||||
progressCallback({ progress: 1, status: 'Exporting project data...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const metadataXml = buildMetadataXml();
|
||||
const projectXml = await buildProjectXml(data, exporterParams);
|
||||
|
||||
progressCallback({ progress: 2, status: 'Creating project file...' });
|
||||
|
||||
const zipProject = new JSZip();
|
||||
|
||||
zipProject.file('metadata.xml', metadataXml);
|
||||
zipProject.file('project.xml', projectXml);
|
||||
|
||||
const projectFile = await zipProject.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
const files: Array<{
|
||||
name: string;
|
||||
url: string;
|
||||
type: 'project' | 'archive';
|
||||
size: number;
|
||||
}> = [
|
||||
{
|
||||
name: `${exporterParams.projectName || `project${projectId}`}.dawproject`,
|
||||
url: URL.createObjectURL(projectFile),
|
||||
type: 'project',
|
||||
size: projectFile.size,
|
||||
},
|
||||
];
|
||||
|
||||
let sampleReport: SampleReport | undefined;
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const zipSamples = new JSZip();
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
|
||||
samples.forEach((s) => {
|
||||
zipSamples.file(s.name, s.data);
|
||||
});
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle samples...' });
|
||||
|
||||
const sampleFile = await zipSamples.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
files.push({
|
||||
name: `${exporterParams.projectName || `project${projectId}`}_samples.zip`,
|
||||
url: URL.createObjectURL(sampleFile),
|
||||
type: 'archive',
|
||||
size: sampleFile.size,
|
||||
});
|
||||
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return {
|
||||
files,
|
||||
sampleReport,
|
||||
} as ExportResult;
|
||||
}
|
||||
|
||||
export default exportDawProject;
|
||||
112
src/lib/exporters/midi.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { Midi } from '@tonejs/midi';
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportStatus,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../types/types';
|
||||
import midiTransformer from '../transformers/midi';
|
||||
import { AbortError } from '../utils';
|
||||
import { collectSamples, ticksToMidiTicks } from './utils';
|
||||
|
||||
async function exportMidi(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
) {
|
||||
progressCallback({ progress: 1, status: 'Exporting project data...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const transformedData = midiTransformer(data, exporterParams);
|
||||
const midi = new Midi();
|
||||
const timeSignature = data.scenesSettings.timeSignature;
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(data);
|
||||
console.log(transformedData);
|
||||
}
|
||||
|
||||
midi.header.setTempo(data.settings.bpm);
|
||||
midi.header.timeSignatures.push({
|
||||
ticks: 0,
|
||||
timeSignature: [timeSignature.numerator, timeSignature.denominator],
|
||||
});
|
||||
|
||||
transformedData.tracks.forEach((track) => {
|
||||
const midiTrack = midi.addTrack();
|
||||
|
||||
midiTrack.name = track.name;
|
||||
midiTrack.channel = 0;
|
||||
|
||||
track.notes.forEach((note) => {
|
||||
midiTrack.addNote({
|
||||
ticks: ticksToMidiTicks(note.position, midi.header.ppq),
|
||||
durationTicks: ticksToMidiTicks(note.duration, midi.header.ppq),
|
||||
velocity: Math.max(0, Math.min(1, note.velocity / 127)),
|
||||
midi: note.note + 12,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// @ts-expect-error wrong typing?
|
||||
const midiBlob = new Blob([midi.toArray()], { type: 'audio/midi' });
|
||||
|
||||
const files: Array<{
|
||||
name: string;
|
||||
url: string;
|
||||
type: 'project' | 'archive';
|
||||
size: number;
|
||||
}> = [
|
||||
{
|
||||
name: `${exporterParams.projectName || `project${projectId}`}.mid`,
|
||||
url: URL.createObjectURL(midiBlob),
|
||||
type: 'project',
|
||||
size: midiBlob.size,
|
||||
},
|
||||
];
|
||||
|
||||
let sampleReport: SampleReport | undefined;
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const zipSamples = new JSZip();
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
|
||||
samples.forEach((s) => {
|
||||
zipSamples.file(s.name, s.data);
|
||||
});
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle samples...' });
|
||||
|
||||
const sampleFile = await zipSamples.generateAsync({ type: 'blob', compression: 'DEFLATE' });
|
||||
|
||||
files.push({
|
||||
name: `${exporterParams.projectName || `project${projectId}`}_samples.zip`,
|
||||
url: URL.createObjectURL(sampleFile),
|
||||
type: 'archive',
|
||||
size: sampleFile.size,
|
||||
});
|
||||
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return {
|
||||
files,
|
||||
sampleReport,
|
||||
} as ExportResult;
|
||||
}
|
||||
|
||||
export default exportMidi;
|
||||
200
src/lib/exporters/reaper/index.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import JSZip from 'jszip';
|
||||
import {
|
||||
ExporterParams,
|
||||
ExportResult,
|
||||
ExportResultFile,
|
||||
ExportStatus,
|
||||
PadCode,
|
||||
ProjectRawData,
|
||||
SampleReport,
|
||||
} from '../../../types/types';
|
||||
import { RprTrack, reaperTransform } from '../../transformers/reaper';
|
||||
import { AbortError } from '../../utils';
|
||||
import { collectSamples, getQuarterNotesPerBar } from '../utils';
|
||||
import { generateReaperProject, ReaperTrack } from './reaperlib';
|
||||
|
||||
function buildTrack(track: RprTrack): ReaperTrack {
|
||||
return {
|
||||
name: track.name,
|
||||
tempo: track.bpm,
|
||||
volume: track.volume,
|
||||
pan: track.pan,
|
||||
sample: track.sampleName
|
||||
? {
|
||||
name: track.sampleName,
|
||||
rate: track.sampleRate,
|
||||
channels: track.sampleChannels,
|
||||
length: track.soundLength,
|
||||
timeStretch: track.timeStretch,
|
||||
timeStretchBars: track.timeStretchBars,
|
||||
timeStretchBpm: track.timeStretchBpm,
|
||||
trimLeft: track.trimLeft,
|
||||
trimRight: track.trimRight,
|
||||
rootNote: track.rootNote,
|
||||
attack: track.attack,
|
||||
release: track.release,
|
||||
playMode: track.playMode,
|
||||
pitch: track.pitch,
|
||||
}
|
||||
: null,
|
||||
timeSignature: track.timeSignature,
|
||||
guid: crypto.randomUUID().toUpperCase(),
|
||||
items: track.items.map((item) => ({
|
||||
position:
|
||||
(item.offset *
|
||||
getQuarterNotesPerBar(track.timeSignature.numerator, track.timeSignature.denominator) *
|
||||
60) /
|
||||
track.bpm,
|
||||
length:
|
||||
(item.sceneBars *
|
||||
getQuarterNotesPerBar(track.timeSignature.numerator, track.timeSignature.denominator) *
|
||||
60) /
|
||||
track.bpm,
|
||||
lengthInBars: item.bars,
|
||||
name: item.sceneName,
|
||||
events: item.notes.map((n) => ({
|
||||
note: n.note,
|
||||
position: n.position,
|
||||
length: n.duration,
|
||||
velocity: n.velocity,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function buildReaperProject(
|
||||
data: ProjectRawData,
|
||||
projectId: string,
|
||||
exporterParams: ExporterParams,
|
||||
) {
|
||||
const transformedData = reaperTransform(data, exporterParams);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
console.log(transformedData);
|
||||
}
|
||||
|
||||
let tracks: ReaperTrack[];
|
||||
if (exporterParams.groupTracks) {
|
||||
const groupedTracks: ReaperTrack[] = [];
|
||||
|
||||
['a', 'b', 'c', 'd'].forEach((group) => {
|
||||
const groupTracks = transformedData.tracks.filter((t) => t.group === group);
|
||||
|
||||
if (groupTracks.length) {
|
||||
groupedTracks.push({
|
||||
...buildTrack({
|
||||
name: `Group ${group.toUpperCase()}`,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
bpm: data.settings.bpm,
|
||||
volume: 1,
|
||||
pan: 0,
|
||||
sampleName: '',
|
||||
sampleChannels: 0,
|
||||
sampleRate: 0,
|
||||
soundLength: 0,
|
||||
attack: 0,
|
||||
release: 0,
|
||||
trimLeft: 0,
|
||||
trimRight: 0,
|
||||
rootNote: 60,
|
||||
timeStretch: 'off',
|
||||
timeStretchBpm: 0,
|
||||
timeStretchBars: 0,
|
||||
soundId: 0,
|
||||
inChokeGroup: false,
|
||||
playMode: 'oneshot',
|
||||
pitch: 0,
|
||||
items: [],
|
||||
pad: 0,
|
||||
padCode: `${group}0` as PadCode,
|
||||
group,
|
||||
}),
|
||||
|
||||
tracks: groupTracks.map(buildTrack),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
tracks = groupedTracks;
|
||||
} else {
|
||||
tracks = transformedData.tracks.map(buildTrack);
|
||||
}
|
||||
|
||||
const rprContent = generateReaperProject(
|
||||
{
|
||||
projectName: exporterParams.projectName || `Project ${projectId}`,
|
||||
tempo: data.settings?.bpm ?? 120,
|
||||
timeSignature: data.scenesSettings.timeSignature,
|
||||
tracks,
|
||||
},
|
||||
exporterParams,
|
||||
);
|
||||
|
||||
return rprContent;
|
||||
}
|
||||
|
||||
async function exportReaper(
|
||||
projectId: string,
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
exporterParams: ExporterParams,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<ExportResult> {
|
||||
let sampleReport: SampleReport | undefined;
|
||||
const files: ExportResultFile[] = [];
|
||||
const zippedProject = new JSZip();
|
||||
const projectName = exporterParams.projectName || `Project${projectId}`;
|
||||
|
||||
progressCallback({ progress: 1, status: 'Preparing REAPER export...' });
|
||||
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const rprContent = buildReaperProject(data, projectId, exporterParams);
|
||||
|
||||
zippedProject.file(`${projectName}/${projectName}.RPP`, rprContent);
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
const { samples, sampleReport: report } = await collectSamples(
|
||||
data,
|
||||
progressCallback,
|
||||
abortSignal,
|
||||
exporterParams.exportAllPadsWithSamples,
|
||||
);
|
||||
samples.forEach((s) => {
|
||||
zippedProject.file(`${projectName}/Media/samples/${s.name}`, s.data);
|
||||
});
|
||||
sampleReport = report;
|
||||
}
|
||||
|
||||
progressCallback({ progress: 90, status: 'Bundle everything...' });
|
||||
|
||||
const zippedProjectFile = await zippedProject.generateAsync({
|
||||
type: 'blob',
|
||||
compression: 'DEFLATE',
|
||||
});
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
const blob = new Blob([rprContent], { type: 'application/octet-stream' });
|
||||
files.push({
|
||||
name: `${projectName}.RPP`,
|
||||
url: URL.createObjectURL(blob),
|
||||
type: 'archive',
|
||||
size: blob.size,
|
||||
});
|
||||
}
|
||||
|
||||
files.push({
|
||||
name: `${projectName}.zip`,
|
||||
url: URL.createObjectURL(zippedProjectFile),
|
||||
type: 'archive',
|
||||
size: zippedProjectFile.size,
|
||||
});
|
||||
|
||||
progressCallback({ progress: 100, status: 'Done' });
|
||||
|
||||
return { files, sampleReport };
|
||||
}
|
||||
|
||||
export default exportReaper;
|
||||
501
src/lib/exporters/reaper/reaperlib.ts
Normal file
@@ -0,0 +1,501 @@
|
||||
import { ExporterParams, TimeSignature } from '../../../types/types';
|
||||
import { getNextColor, getQuarterNotesPerBar, TICKS_PER_BEAT, ticksToMidiTicks } from '../utils';
|
||||
import { buildVstState } from './sampler';
|
||||
|
||||
export type ReaperMidiEvent = {
|
||||
note: number;
|
||||
position: number;
|
||||
length: number;
|
||||
velocity: number;
|
||||
};
|
||||
|
||||
export type ReaperMidiItem = {
|
||||
position: number;
|
||||
length: number;
|
||||
lengthInBars: number;
|
||||
name?: string;
|
||||
events: ReaperMidiEvent[];
|
||||
};
|
||||
|
||||
export type ReaperSample = {
|
||||
name: string;
|
||||
rate: number;
|
||||
channels: number;
|
||||
length: number;
|
||||
timeStretch: string;
|
||||
timeStretchBars: number;
|
||||
timeStretchBpm: number;
|
||||
trimLeft: number;
|
||||
trimRight: number;
|
||||
rootNote: number;
|
||||
attack: number;
|
||||
release: number;
|
||||
playMode: string;
|
||||
pitch: number;
|
||||
};
|
||||
|
||||
export type ReaperTrack = {
|
||||
name: string;
|
||||
guid: string;
|
||||
tempo: number;
|
||||
volume: number;
|
||||
pan: number;
|
||||
sample: ReaperSample | null;
|
||||
timeSignature: TimeSignature;
|
||||
tracks?: ReaperTrack[];
|
||||
items?: ReaperMidiItem[];
|
||||
};
|
||||
|
||||
export type ReaperProject = {
|
||||
projectName?: string;
|
||||
tempo: number;
|
||||
timeSignature: TimeSignature;
|
||||
tracks?: ReaperTrack[];
|
||||
};
|
||||
|
||||
type ReaperFileElem = {
|
||||
name: string;
|
||||
attrs?: (string | number)[];
|
||||
content?: (ReaperFileElem | (string | number)[])[];
|
||||
};
|
||||
|
||||
const PPQ = 960; // default reaper midi ppq
|
||||
|
||||
function hexToReaperColor(hexColor: string, alpha = 0xff) {
|
||||
hexColor = hexColor.replace('#', '');
|
||||
|
||||
const red = parseInt(hexColor.substring(0, 2), 16);
|
||||
const green = parseInt(hexColor.substring(2, 4), 16);
|
||||
const blue = parseInt(hexColor.substring(4, 6), 16);
|
||||
|
||||
return (alpha << 24) | (blue << 16) | (green << 8) | red;
|
||||
}
|
||||
|
||||
function renderLine(name: string, attrs: (string | number)[] = []) {
|
||||
return `${name} ${attrs
|
||||
.map((block) => {
|
||||
if (typeof block === 'string' && block.match(/\w+-\w+-\w+-\w+-\w+/)) {
|
||||
return `{${block}}`;
|
||||
}
|
||||
|
||||
return typeof block === 'string' ? `"${block}"` : block;
|
||||
})
|
||||
.join(' ')}`;
|
||||
}
|
||||
|
||||
function addFxChain(root: ReaperFileElem['content'], rprTrack: ReaperTrack) {
|
||||
if (!rprTrack.sample?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = buildVstState({
|
||||
filePath: `Media/samples/${rprTrack.sample.name}`,
|
||||
sampleLengthMs: rprTrack.sample.length * 1000,
|
||||
rootNote: rprTrack.sample.rootNote,
|
||||
attack: rprTrack.sample.attack,
|
||||
release: rprTrack.sample.release,
|
||||
});
|
||||
|
||||
root?.push({
|
||||
name: 'FXCHAIN',
|
||||
content: [
|
||||
['WNDRECT', 32, 117, 1037, 681],
|
||||
['SHOW', 0],
|
||||
['LASTSEL', 0],
|
||||
['DOCKED', 0],
|
||||
['BYPASS', 0, 0, 0],
|
||||
{
|
||||
name: 'VST',
|
||||
attrs: [
|
||||
'VSTi: ReaSamplOmatic5000 (Cockos)',
|
||||
'reasamplomatic.vst.so',
|
||||
0,
|
||||
'',
|
||||
'1920167789<56535472736F6D72656173616D706C6F>',
|
||||
'',
|
||||
],
|
||||
content: [[result.header], [result.body], ['AAAQAAAA']],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function addTrackItem(
|
||||
root: ReaperFileElem['content'],
|
||||
rprItem: ReaperMidiItem,
|
||||
rprTrack: ReaperTrack,
|
||||
iid: number,
|
||||
) {
|
||||
if (rprItem.events.length === 0) {
|
||||
return iid;
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
let totalTicks = 0;
|
||||
const barLength = getQuarterNotesPerBar(
|
||||
rprTrack.timeSignature.numerator,
|
||||
rprTrack.timeSignature.denominator,
|
||||
);
|
||||
|
||||
const clipLengthInTicks = rprItem.lengthInBars * barLength * TICKS_PER_BEAT;
|
||||
|
||||
const events = rprItem.events
|
||||
.flat()
|
||||
.filter((evt) => evt.position < clipLengthInTicks)
|
||||
.reduce(
|
||||
(acc, evt, index) => {
|
||||
const nextEvent = rprItem.events[index + 1];
|
||||
let noteLength = evt.length;
|
||||
|
||||
if (
|
||||
nextEvent &&
|
||||
nextEvent.note === evt.note &&
|
||||
nextEvent.position < evt.position + evt.length
|
||||
) {
|
||||
// prevent notes overlapping
|
||||
noteLength = nextEvent.position - evt.position;
|
||||
}
|
||||
|
||||
// prevent notes going beyond the item length
|
||||
if (evt.position + noteLength > clipLengthInTicks) {
|
||||
noteLength = evt.position + noteLength - clipLengthInTicks;
|
||||
}
|
||||
|
||||
const noteOn = [
|
||||
'e',
|
||||
ticksToMidiTicks(evt.position - offset, PPQ),
|
||||
'90',
|
||||
evt.note.toString(16),
|
||||
evt.velocity.toString(16),
|
||||
];
|
||||
const noteOff = ['e', ticksToMidiTicks(noteLength, PPQ), '80', evt.note.toString(16), '00'];
|
||||
|
||||
offset = evt.position + noteLength;
|
||||
totalTicks += (noteOn[1] as number) + (noteOff[1] as number);
|
||||
|
||||
return acc.concat([noteOn, noteOff]);
|
||||
},
|
||||
[] as (string | number)[][],
|
||||
);
|
||||
|
||||
const barInTicks = PPQ * barLength;
|
||||
|
||||
events.push(['E', Math.max(barInTicks * rprItem.lengthInBars - totalTicks, 0), 'b0', '7b', '00']);
|
||||
|
||||
const item = {
|
||||
name: 'ITEM',
|
||||
content: [
|
||||
['POSITION', rprItem.position],
|
||||
['SNAPOFFS', 0],
|
||||
['LENGTH', rprItem.length],
|
||||
['LOOP', 1],
|
||||
['ALLTAKES', 0],
|
||||
['FADEIN', 1, 0, 0, 1, 0, 0, 0],
|
||||
['FADEOUT', 1, 0, 0, 1, 0, 0, 0],
|
||||
['MUTE', 0, 0],
|
||||
['SEL', 0],
|
||||
['IGUID', crypto.randomUUID().toUpperCase()],
|
||||
['IID', iid++],
|
||||
['NAME', rprItem.name || `MIDI Item ${iid}`],
|
||||
['VOLPAN', 1, 0, 1, -1],
|
||||
['SOFFS', 0],
|
||||
['PLAYRATE', 1, 1, 0, -1, 0, 0.0025],
|
||||
['CHANMODE', 0],
|
||||
['GUID', crypto.randomUUID().toUpperCase()],
|
||||
{
|
||||
name: 'SOURCE',
|
||||
attrs: ['MIDI'],
|
||||
content: [
|
||||
['HASDATA', 1, PPQ, 'QN'],
|
||||
['CCINTERP', 32],
|
||||
['POOLEDEVTS', crypto.randomUUID().toUpperCase()],
|
||||
...events,
|
||||
['CCINTERP', 32],
|
||||
['CHASE_CC_TAKEOFFS', 1],
|
||||
['GUID', crypto.randomUUID().toUpperCase()],
|
||||
['IGNTEMPO', 0, rprTrack.tempo, 4, 4],
|
||||
['SRCCOLOR', 6],
|
||||
['EVTFILTER', 0, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, -1, 0, -1, 0, -1, -1],
|
||||
['VELLANE', -1, 100, 0, 0, 1],
|
||||
['CFGEDITVIEW', 0, 0.226823, 65, 12, 0, 0, 0, 0, 0, 0.5],
|
||||
['KEYSNAP', 0],
|
||||
['TRACKSEL', 0],
|
||||
[
|
||||
'CFGEDIT',
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
0.125,
|
||||
753,
|
||||
516,
|
||||
1975,
|
||||
1078,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0.5,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
64,
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
root?.push(item);
|
||||
|
||||
return iid;
|
||||
}
|
||||
|
||||
const addTrack = (
|
||||
root: ReaperFileElem['content'],
|
||||
rprTrack: ReaperTrack,
|
||||
iid: number,
|
||||
endOfGroup: boolean,
|
||||
exporterParams: ExporterParams,
|
||||
) => {
|
||||
let isBus = [0, 0];
|
||||
|
||||
if (rprTrack.tracks?.length) {
|
||||
isBus = [1, 1];
|
||||
}
|
||||
|
||||
if (endOfGroup) {
|
||||
isBus = [2, -1];
|
||||
}
|
||||
|
||||
const newTrack: ReaperFileElem = {
|
||||
name: 'TRACK',
|
||||
attrs: [rprTrack.guid],
|
||||
content: [
|
||||
['NAME', rprTrack.name || 'Track'],
|
||||
['PEAKCOL', hexToReaperColor(getNextColor())],
|
||||
['BEAT', -1],
|
||||
['AUTOMODE', 0],
|
||||
['PANLAWFLAGS', 3],
|
||||
['VOLPAN', rprTrack.volume, rprTrack.pan, -1, -1, 1],
|
||||
['MUTESOLO', 0, 0, 0],
|
||||
['IPHASE', 0],
|
||||
['PLAYOFFS', 0, 1],
|
||||
['ISBUS', ...isBus],
|
||||
['BUSCOMP', 0, 0, 0, 0, 0],
|
||||
['SHOWINMIX', 1, 0.6667, 0.5, 1, 0.5, 0, 0, 0, 0],
|
||||
['FIXEDLANES', 9, 0, 0, 0, 0],
|
||||
['SEL', 0],
|
||||
['REC', 0, 0, 1, 0, 0, 0, 0, 0],
|
||||
['VU', 64],
|
||||
['TRACKHEIGHT', 0, 0, 0, 0, 0, 0, 0],
|
||||
['INQ', 0, 0, 0, 0.5, 100, 0, 0, 100],
|
||||
['NCHAN', 2],
|
||||
['FX', 1],
|
||||
['TRACKID', rprTrack.guid],
|
||||
['PERF', 0],
|
||||
['MIDIOUT', -1],
|
||||
['MAINSEND', 1, 0],
|
||||
],
|
||||
};
|
||||
|
||||
if (exporterParams.includeArchivedSamples) {
|
||||
addFxChain(newTrack.content, rprTrack);
|
||||
}
|
||||
|
||||
if (rprTrack.items) {
|
||||
rprTrack.items.forEach((rprItem) => {
|
||||
iid = addTrackItem(newTrack.content, rprItem, rprTrack, iid);
|
||||
});
|
||||
}
|
||||
|
||||
root?.push(newTrack);
|
||||
|
||||
rprTrack.tracks?.forEach((_track, idx) => {
|
||||
iid = addTrack(root, _track, iid, idx === rprTrack.tracks!.length - 1, exporterParams);
|
||||
});
|
||||
|
||||
return iid;
|
||||
};
|
||||
|
||||
function createReaperProject(
|
||||
{ tempo = 120, timeSignature, tracks = [] }: ReaperProject,
|
||||
exporterParams: ExporterParams,
|
||||
) {
|
||||
let _iid = 1;
|
||||
|
||||
const _root: ReaperFileElem[] = [
|
||||
{
|
||||
name: 'REAPER_PROJECT',
|
||||
attrs: ['0.1', '7.48/unknown-x86_64', Math.round(Date.now() / 1000)],
|
||||
content: [
|
||||
{
|
||||
name: 'NOTES',
|
||||
attrs: [0, 2],
|
||||
},
|
||||
['RIPPLE', 0, 0],
|
||||
['GROUPOVERRIDE', 0, 0, 0, 0],
|
||||
['AUTOXFADE', 129],
|
||||
['ENVATTACH', 3],
|
||||
['POOLEDENVATTACH', 0],
|
||||
['MIXERUIFLAGS', 11, 48],
|
||||
['ENVFADESZ10', 40],
|
||||
['PEAKGAIN', 1],
|
||||
['FEEDBACK', 0],
|
||||
['PANLAW', 1],
|
||||
['PROJOFFS', 0, 0, 0],
|
||||
['MAXPROJLEN', 0, 0],
|
||||
['GRID', 3199, 8, 1, 8, 1, 0, 0, 0],
|
||||
['TIMEMODE', 1, 5, -1, 30, 0, 0, -1],
|
||||
['VIDEO_CONFIG', 0, 0, 65792],
|
||||
['PANMODE', 3],
|
||||
['PANLAWFLAGS', 3],
|
||||
['CURSOR', 0],
|
||||
['ZOOM', 100, 0, 0],
|
||||
['VZOOMEX', 6, 0],
|
||||
['USE_REC_CFG', 0],
|
||||
['RECMODE', 1],
|
||||
['SMPTESYNC', 0, 30, 100, 40, 1000, 300, 0, 0, 1, 0, 0],
|
||||
['LOOP', 1],
|
||||
['LOOPGRAN', 0],
|
||||
['RECORD_PATH', 'Media', ''],
|
||||
{
|
||||
name: 'RECORD_CFG',
|
||||
content: [['ZXZhdxgAAQ==']],
|
||||
},
|
||||
{
|
||||
name: 'APPLYFX_CFG',
|
||||
},
|
||||
['RENDER_FILE', ''],
|
||||
['RENDER_PATTERN', ''],
|
||||
['RENDER_FMT', 0, 2, 0],
|
||||
['RENDER_1X', 0],
|
||||
['RENDER_RANGE', 1, 0, 0, 0, 1000],
|
||||
['RENDER_RESAMPLE', 3, 0, 1],
|
||||
['RENDER_ADDTOPROJ', 0],
|
||||
['RENDER_STEMS', 0],
|
||||
['RENDER_DITHER', 0],
|
||||
['TIMELOCKMODE', 1],
|
||||
['TEMPOENVLOCKMODE', 1],
|
||||
['ITEMMIX', 1],
|
||||
['DEFPITCHMODE', 589824, 0],
|
||||
['TAKELANE', 1],
|
||||
['SAMPLERATE', 44100, 0, 0],
|
||||
{
|
||||
name: 'RENDER_CFG',
|
||||
content: [['ZXZhdxgAAQ==']],
|
||||
},
|
||||
['LOCK', 1],
|
||||
{
|
||||
name: 'METRONOME',
|
||||
attrs: [6, 2],
|
||||
content: [
|
||||
['VOL', 0.25, 0.125],
|
||||
['BEATLEN', 4],
|
||||
['FREQ', 1760, 880, 1],
|
||||
['SAMPLES', '', '', '', ''],
|
||||
['SPLIGNORE', 0, 0],
|
||||
['SPLDEF', 2, 660, '', 0, ''],
|
||||
['SPLDEF', 3, 440, '', 0, ''],
|
||||
['PATTERN', 0, 169],
|
||||
['PATTERNSTR', 'ABBB'],
|
||||
['MULT', 1],
|
||||
],
|
||||
},
|
||||
['GLOBAL_AUTO', -1],
|
||||
['TEMPO', tempo, timeSignature.numerator, timeSignature.denominator, 0],
|
||||
['PLAYRATE', 1, 0, 0.25, 4],
|
||||
['SELECTION', 0, 0],
|
||||
['SELECTION2', 0, 0],
|
||||
['MASTERAUTOMODE', 0],
|
||||
['MASTERTRACKHEIGHT', 0, 0],
|
||||
['MASTERPEAKCOL', 16576],
|
||||
['MASTERMUTESOLO', 0],
|
||||
['MASTERTRACKVIEW', 0, 0.6667, 0.5, 0.5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
|
||||
['MASTERHWOUT', 0, 0, 1, 0, 0, 0, 0, -1],
|
||||
['MASTER_NCH', 2, 2],
|
||||
['MASTER_VOLUME', 1, 0, -1, -1, 1],
|
||||
['MASTER_PANMODE', 3],
|
||||
['MASTER_PANLAWFLAGS', 3],
|
||||
['MASTER_FX', 1],
|
||||
['MASTER_SEL', 0],
|
||||
{
|
||||
name: 'MASTERPLAYSPEEDENV',
|
||||
content: [
|
||||
['EGUID', crypto.randomUUID().toUpperCase()],
|
||||
['ACT', 0, -1],
|
||||
['VIS', 0, 1, 1],
|
||||
['LANEHEIGHT', 0, 0],
|
||||
['ARM', 0],
|
||||
['DEFSHAPE', 0, -1, -1],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'TEMPOENVEX',
|
||||
content: [
|
||||
['EGUID', crypto.randomUUID().toUpperCase()],
|
||||
['ACT', 1, -1],
|
||||
['VIS', 1, 0, 1],
|
||||
['LANEHEIGHT', 0, 0],
|
||||
['ARM', 0],
|
||||
['DEFSHAPE', 1, -1, -1],
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'PROJBAY',
|
||||
content: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
tracks.forEach((_track) => {
|
||||
_iid = addTrack(_root[0].content, _track, _iid, false, exporterParams);
|
||||
});
|
||||
|
||||
return {
|
||||
toString(rootElems: any[] = _root, offset = 0) {
|
||||
let result = '';
|
||||
|
||||
for (const elem of rootElems) {
|
||||
if (Array.isArray(elem)) {
|
||||
// biome-ignore lint/style/useTemplate: too messy
|
||||
result += `${' '.repeat(2 * offset)}` + renderLine(elem[0], elem.slice(1)) + '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
// biome-ignore lint/style/useTemplate: too messy
|
||||
result += `${' '.repeat(2 * offset)}<` + renderLine(elem.name, elem.attrs) + '\n';
|
||||
|
||||
if (elem.content && elem.content.length > 0) {
|
||||
result += this.toString(elem.content, offset + 1);
|
||||
}
|
||||
|
||||
result += `${' '.repeat(2 * offset)}>\n`;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function generateReaperProject(
|
||||
input: ReaperProject,
|
||||
exporterParams: ExporterParams,
|
||||
): string {
|
||||
const reaperProject = createReaperProject(input, exporterParams);
|
||||
|
||||
return reaperProject.toString();
|
||||
}
|
||||
115
src/lib/exporters/reaper/sampler.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
const VST_HEADER = [
|
||||
109, 111, 115, 114, 238, 94, 237, 254, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
|
||||
0, 0, 0, 0, 90, 1, 0, 0, 1, 0, 0, 0, 0, 0, 16, 0,
|
||||
];
|
||||
|
||||
const VST_BODY = [
|
||||
0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 240, 63, 154, 153, 153, 153, 153, 153, 177, 63, 205, 204, 204, 204, 204,
|
||||
204, 235, 63, 0, 0, 0, 0, 0, 0, 0, 0, 28, 199, 113, 28, 199, 113, 220, 63, 252, 169, 241, 210, 77,
|
||||
98, 64, 63, 252, 169, 241, 210, 77, 98, 64, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0, 224, 63, 1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 63, 64, 0, 0, 0, 85, 85, 85, 85, 85, 85, 197, 63, 255, 255,
|
||||
255, 255, 8, 4, 2, 129, 64, 32, 128, 63, 0, 0, 0, 0, 0, 0, 240, 63, 0, 0, 0, 0, 0, 0, 240, 63, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 206,
|
||||
164, 33, 33, 26, 101, 144, 63, 0, 0, 0, 0, 0, 0, 240, 63, 252, 169, 241, 210, 77, 98, 48, 63, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0,
|
||||
];
|
||||
|
||||
function int32ToBytes(value: number): Uint8Array {
|
||||
const bytes = new Uint8Array(4);
|
||||
|
||||
bytes[0] = value & 0xff;
|
||||
bytes[1] = (value >> 8) & 0xff;
|
||||
bytes[2] = (value >> 16) & 0xff;
|
||||
bytes[3] = (value >> 24) & 0xff;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function doubleToBytes(num: number): number[] {
|
||||
const buffer = new ArrayBuffer(8);
|
||||
const view = new DataView(buffer);
|
||||
|
||||
view.setFloat64(0, num, true);
|
||||
const bytes = [];
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
bytes.push(view.getUint8(i));
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function dbToBytes(dbValue: number): number[] {
|
||||
// convert dB to linear gain: 10^(dB/20)
|
||||
const linearGain = 10 ** (dbValue / 20);
|
||||
|
||||
return doubleToBytes(linearGain);
|
||||
}
|
||||
|
||||
function msToBytes(msValue: number, sampleLengthMs: number): number[] {
|
||||
const attackNormalized = msValue / 255;
|
||||
const maxAttack = sampleLengthMs / 2000;
|
||||
const cappedAttackMs = Math.min(attackNormalized, maxAttack);
|
||||
|
||||
return doubleToBytes(cappedAttackMs);
|
||||
}
|
||||
|
||||
function midiNoteToBytes(midiNote: number): number[] {
|
||||
const referenceNote = 60; // C4
|
||||
const referenceValue = 0.125;
|
||||
const valuePerSemitone = 0.075 / 12; // 0.00625
|
||||
|
||||
const semitoneOffset = referenceNote - midiNote;
|
||||
const pluginValue = referenceValue + semitoneOffset * valuePerSemitone;
|
||||
|
||||
return doubleToBytes(pluginValue);
|
||||
}
|
||||
|
||||
function bytesToBase64(bytes: Array<number>): string {
|
||||
let binary = '';
|
||||
const len = bytes.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i]);
|
||||
}
|
||||
|
||||
return btoa(binary);
|
||||
}
|
||||
|
||||
export function buildVstState({
|
||||
sampleLengthMs,
|
||||
filePath,
|
||||
volume = 0,
|
||||
attack = 0,
|
||||
release = 0,
|
||||
rootNote = 60,
|
||||
}: {
|
||||
filePath: string;
|
||||
sampleLengthMs: number;
|
||||
volume?: number;
|
||||
attack?: number;
|
||||
release?: number;
|
||||
rootNote?: number;
|
||||
}) {
|
||||
const encoder = new TextEncoder();
|
||||
const bufferHeader = [...VST_HEADER];
|
||||
const bufferBody = [...VST_BODY];
|
||||
|
||||
bufferBody.splice(0, 8, ...dbToBytes(volume)); // volume
|
||||
bufferBody.splice(72, 8, ...msToBytes(attack, sampleLengthMs)); // atack
|
||||
bufferBody.splice(80, 8, ...msToBytes(release, sampleLengthMs)); // release
|
||||
bufferBody.splice(40, 8, ...midiNoteToBytes(rootNote)); // root note
|
||||
bufferBody.splice(88, 8, ...doubleToBytes(1)); // obey note off
|
||||
bufferBody[128] = 2; // play mode: key
|
||||
|
||||
const fullBody = [...encoder.encode(filePath), 0, ...bufferBody];
|
||||
|
||||
bufferHeader.splice(32, 4, ...int32ToBytes(fullBody.length));
|
||||
|
||||
return {
|
||||
header: bytesToBase64(bufferHeader),
|
||||
body: bytesToBase64(fullBody),
|
||||
};
|
||||
}
|
||||
358
src/lib/exporters/utils.ts
Normal file
@@ -0,0 +1,358 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { unzippedBackupAtom } from '~/atoms/droppedProjectFile';
|
||||
import { ExportStatus, ProjectRawData, SoundInfo } from '../../types/types';
|
||||
import { getFile, getFileNodeByPath } from '../midi/fs';
|
||||
import { pcmToWavBlob } from '../pcmToWav';
|
||||
import { store } from '../store';
|
||||
import { AbortError, audioFormatAsBitDepth, findSoundIdByPad } from '../utils';
|
||||
|
||||
let _colorIndex = 0;
|
||||
|
||||
export const TICKS_PER_BEAT = 96; // (384 / 4 beats)
|
||||
const COLORS = [
|
||||
'#B2B0E8',
|
||||
'#E27D60',
|
||||
'#26A693',
|
||||
'#E8A87C',
|
||||
'#85C1A9',
|
||||
'#C38D9E',
|
||||
'#41B3A3',
|
||||
'#F2B880',
|
||||
'#7DAF9C',
|
||||
'#F47261',
|
||||
'#9D6A89',
|
||||
'#5AA9A4',
|
||||
'#7A2A80',
|
||||
'#8FB996',
|
||||
'#47B267',
|
||||
'#B089A3',
|
||||
'#6CA6A3',
|
||||
'#A0C4B0',
|
||||
'#F5A97F',
|
||||
'#BBA0C0',
|
||||
'#79B2B2',
|
||||
];
|
||||
|
||||
function extractPcmFromWav(wavData: Uint8Array): Uint8Array {
|
||||
const view = new DataView(wavData.buffer, wavData.byteOffset, wavData.byteLength);
|
||||
|
||||
const riff = String.fromCharCode(wavData[0], wavData[1], wavData[2], wavData[3]);
|
||||
if (riff !== 'RIFF') {
|
||||
throw new Error('Invalid WAV file: missing RIFF header');
|
||||
}
|
||||
|
||||
const wave = String.fromCharCode(wavData[8], wavData[9], wavData[10], wavData[11]);
|
||||
if (wave !== 'WAVE') {
|
||||
throw new Error('Invalid WAV file: missing WAVE format');
|
||||
}
|
||||
|
||||
let offset = 12;
|
||||
while (offset < wavData.length - 8) {
|
||||
const chunkId = String.fromCharCode(
|
||||
wavData[offset],
|
||||
wavData[offset + 1],
|
||||
wavData[offset + 2],
|
||||
wavData[offset + 3],
|
||||
);
|
||||
const chunkSize = view.getUint32(offset + 4, true);
|
||||
|
||||
if (chunkId === 'data') {
|
||||
return wavData.slice(offset + 8, offset + 8 + chunkSize);
|
||||
}
|
||||
|
||||
offset += 8 + chunkSize;
|
||||
if (chunkSize % 2 !== 0) {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Invalid WAV file: data chunk not found');
|
||||
}
|
||||
|
||||
export function parseWavMetadata(wavData: Uint8Array) {
|
||||
const view = new DataView(wavData.buffer, wavData.byteOffset, wavData.byteLength);
|
||||
|
||||
const riff = String.fromCharCode(wavData[0], wavData[1], wavData[2], wavData[3]);
|
||||
if (riff !== 'RIFF') {
|
||||
throw new Error('Invalid WAV file: missing RIFF header');
|
||||
}
|
||||
|
||||
const wave = String.fromCharCode(wavData[8], wavData[9], wavData[10], wavData[11]);
|
||||
if (wave !== 'WAVE') {
|
||||
throw new Error('Invalid WAV file: missing WAVE format');
|
||||
}
|
||||
|
||||
let offset = 12;
|
||||
let channels = 1;
|
||||
let samplerate = 44100;
|
||||
let bitsPerSample = 16;
|
||||
let audioFormat = 1;
|
||||
let rootNote = 60;
|
||||
let teMeta: Record<string, unknown> | null = null;
|
||||
|
||||
while (offset < wavData.length - 8) {
|
||||
const chunkId = String.fromCharCode(
|
||||
wavData[offset],
|
||||
wavData[offset + 1],
|
||||
wavData[offset + 2],
|
||||
wavData[offset + 3],
|
||||
);
|
||||
const chunkSize = view.getUint32(offset + 4, true);
|
||||
|
||||
if (chunkId === 'fmt ') {
|
||||
audioFormat = view.getUint16(offset + 8, true);
|
||||
channels = view.getUint16(offset + 10, true);
|
||||
samplerate = view.getUint32(offset + 12, true);
|
||||
bitsPerSample = view.getUint16(offset + 22, true);
|
||||
}
|
||||
|
||||
if (chunkId === 'smpl') {
|
||||
rootNote = view.getUint32(offset + 20, true);
|
||||
}
|
||||
|
||||
if (chunkId === 'LIST') {
|
||||
const listData = wavData.subarray(offset + 8, offset + 8 + chunkSize);
|
||||
const listType = String.fromCharCode(listData[0], listData[1], listData[2], listData[3]);
|
||||
if (listType === 'INFO') {
|
||||
let subOffset = 4;
|
||||
while (subOffset < listData.length - 8) {
|
||||
const subId = String.fromCharCode(
|
||||
listData[subOffset],
|
||||
listData[subOffset + 1],
|
||||
listData[subOffset + 2],
|
||||
listData[subOffset + 3],
|
||||
);
|
||||
const subSize = new DataView(
|
||||
listData.buffer,
|
||||
listData.byteOffset + subOffset + 4,
|
||||
4,
|
||||
).getUint32(0, true);
|
||||
|
||||
if (subId === 'TNGE') {
|
||||
const raw = listData.subarray(subOffset + 8, subOffset + 8 + subSize);
|
||||
const jsonStr = new TextDecoder('ascii')
|
||||
.decode(raw)
|
||||
.replace(/\0/g, '')
|
||||
.trim();
|
||||
try {
|
||||
teMeta = JSON.parse(jsonStr);
|
||||
} catch {
|
||||
console.warn('Failed to parse TNGE metadata JSON:', jsonStr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
subOffset += 8 + subSize;
|
||||
if (subSize % 2 !== 0) {
|
||||
subOffset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += 8 + chunkSize;
|
||||
if (chunkSize % 2 !== 0) {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let format: 's16' | 's24' | 'float';
|
||||
if (audioFormat === 3 && bitsPerSample === 32) {
|
||||
format = 'float';
|
||||
} else if (bitsPerSample === 24) {
|
||||
format = 's24';
|
||||
} else {
|
||||
format = 's16';
|
||||
}
|
||||
|
||||
return {
|
||||
channels,
|
||||
samplerate,
|
||||
format,
|
||||
rootNote: (teMeta?.['sound.rootnote'] as number) ?? rootNote,
|
||||
teMeta,
|
||||
};
|
||||
}
|
||||
|
||||
export async function downloadPcm(
|
||||
soundId: number,
|
||||
progressCallback?: (bytesRead: number, totalRemaining: number) => void,
|
||||
) {
|
||||
const backupZip = store.get(unzippedBackupAtom);
|
||||
|
||||
if (backupZip) {
|
||||
const wavFile = Object.values(backupZip.files).find((file) =>
|
||||
file.name.startsWith(`/sounds/${String(soundId).padStart(3, '0')} `),
|
||||
);
|
||||
|
||||
if (!wavFile) {
|
||||
throw new Error(`Sound file for sound ID ${soundId} not found in backup`);
|
||||
}
|
||||
|
||||
const wavData = await wavFile.async('uint8array');
|
||||
return extractPcmFromWav(wavData);
|
||||
}
|
||||
|
||||
const fileNode = await getFileNodeByPath(`/sounds/${String(soundId).padStart(3, '0')}.pcm`);
|
||||
|
||||
if (!fileNode) {
|
||||
throw new Error(`Sound file for sound ID ${soundId} not found`);
|
||||
}
|
||||
|
||||
const fileData = await getFile(fileNode.nodeId, progressCallback);
|
||||
|
||||
return fileData.data;
|
||||
}
|
||||
|
||||
export function getSampleName(name: string | undefined, soundId: number, extension = true) {
|
||||
if (soundId >= 1000) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (soundId === 0 && !name) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const id = soundId.toString().padStart(3, '0');
|
||||
const n = name ? `${id} ${name}` : `${id} sample`;
|
||||
|
||||
return extension ? `${n}.wav` : n;
|
||||
}
|
||||
|
||||
function getSoundsInfoFromProject(data: ProjectRawData, exportAllPadsWithSamples = false) {
|
||||
const snds: SoundInfo[] = [];
|
||||
const existingSounds = new Set<number>();
|
||||
const usedSoundIds = new Set<number>();
|
||||
|
||||
for (const scene of data.scenes) {
|
||||
for (const pattern of scene.patterns) {
|
||||
if (pattern.notes.length > 0) {
|
||||
const sid = findSoundIdByPad(pattern.pad, data.pads);
|
||||
if (sid) {
|
||||
usedSoundIds.add(sid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const group in data.pads) {
|
||||
for (const pad of data.pads[group]) {
|
||||
if (pad.soundId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!exportAllPadsWithSamples && !usedSoundIds.has(pad.soundId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (existingSounds.has(pad.soundId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const soundMeta = data.sounds.find((s) => s.id === pad.soundId);
|
||||
if (!soundMeta) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snds.push({ soundId: pad.soundId, soundMeta: soundMeta.meta });
|
||||
existingSounds.add(pad.soundId);
|
||||
}
|
||||
}
|
||||
|
||||
return snds;
|
||||
}
|
||||
|
||||
export async function collectSamples(
|
||||
data: ProjectRawData,
|
||||
progressCallback: ({ progress, status }: ExportStatus) => void,
|
||||
abortSignal: AbortSignal,
|
||||
exportAllPadsWithSamples = false,
|
||||
) {
|
||||
const projectSounds = getSoundsInfoFromProject(data, exportAllPadsWithSamples);
|
||||
|
||||
const samples: { name: string; data: Blob }[] = [];
|
||||
const downloaded: string[] = [];
|
||||
const missing: { name: string; error: string }[] = [];
|
||||
const percentStart = 3;
|
||||
const totalSounds = projectSounds.length || 1;
|
||||
const percentPerSound = 80 / totalSounds;
|
||||
let cnt = 0;
|
||||
|
||||
for (const snd of projectSounds) {
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const fileName = getSampleName(snd.soundMeta.name, snd.soundId);
|
||||
|
||||
try {
|
||||
progressCallback({
|
||||
progress: percentStart + percentPerSound * cnt,
|
||||
status: `Downloading sound: ${fileName}`,
|
||||
});
|
||||
|
||||
const result = await downloadPcm(snd.soundId, (bytesRead, totalRemaining) => {
|
||||
if (abortSignal.aborted) {
|
||||
throw new AbortError();
|
||||
}
|
||||
|
||||
const currentSoundProgress = bytesRead / (totalRemaining / percentPerSound);
|
||||
progressCallback({
|
||||
progress: percentStart + percentPerSound * cnt + currentSoundProgress,
|
||||
status: `Downloading sound: ${fileName}`,
|
||||
});
|
||||
});
|
||||
|
||||
const wavBlob = pcmToWavBlob(
|
||||
result,
|
||||
snd.soundMeta.samplerate,
|
||||
audioFormatAsBitDepth(snd.soundMeta.format),
|
||||
snd.soundMeta.channels,
|
||||
);
|
||||
|
||||
samples.push({
|
||||
name: fileName,
|
||||
data: wavBlob,
|
||||
});
|
||||
|
||||
downloaded.push(fileName);
|
||||
} catch (err) {
|
||||
// only report when not aborted
|
||||
if (!abortSignal.aborted) {
|
||||
console.error(err);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
|
||||
missing.push({
|
||||
name: fileName,
|
||||
error: err instanceof Error ? err.message : 'Unknown error',
|
||||
});
|
||||
} finally {
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
const sampleReport = { downloaded, missing };
|
||||
|
||||
progressCallback({
|
||||
progress: projectSounds.length === 0 ? percentStart + 80 : percentStart + percentPerSound * cnt,
|
||||
status: 'Sample collection completed',
|
||||
sampleReport,
|
||||
});
|
||||
|
||||
return { samples, sampleReport };
|
||||
}
|
||||
|
||||
export function getNextColor() {
|
||||
const color = COLORS[_colorIndex];
|
||||
_colorIndex = (_colorIndex + 1) % COLORS.length;
|
||||
return color;
|
||||
}
|
||||
|
||||
export function ticksToMidiTicks(ticks: number, ppq = 480) {
|
||||
return Math.round((ticks / TICKS_PER_BEAT) * ppq);
|
||||
}
|
||||
|
||||
export function getQuarterNotesPerBar(numerator: number, denominator: number) {
|
||||
return (4 / denominator) * numerator;
|
||||
}
|
||||
5
src/lib/ga.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export function trackEvent(action: string, eventParams?: any) {
|
||||
if (import.meta.env.VITE_GA_ID) {
|
||||
window.gtag('event', action, eventParams);
|
||||
}
|
||||
}
|
||||
31
src/lib/midi/constants.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export const TE_SYSEX = {
|
||||
STATUS_OK: 0,
|
||||
STATUS_ERROR: 1,
|
||||
STATUS_COMMAND_NOT_FOUND: 2,
|
||||
STATUS_BAD_REQUEST: 3,
|
||||
STATUS_SPECIFIC_ERROR_START: 16,
|
||||
STATUS_SPECIFIC_SUCCESS_START: 64,
|
||||
};
|
||||
|
||||
export const BIT_IS_REQUEST = 64;
|
||||
export const BIT_REQUEST_ID_AVAILABLE = 32;
|
||||
export const MIDI_SYSEX_START = 240;
|
||||
export const MIDI_SYSEX_END = 247;
|
||||
export const TE_MIDI_ID_0 = 0;
|
||||
export const TE_MIDI_ID_1 = 32;
|
||||
export const TE_MIDI_ID_2 = 118;
|
||||
export const MIDI_SYSEX_TE = 64;
|
||||
|
||||
export const IDENTITY_SYSEX = [0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7];
|
||||
|
||||
export const TE_SYSEX_GREET = 1;
|
||||
export const TE_SYSEX_FILE = 5;
|
||||
export const TE_SYSEX_FILE_INIT = 1;
|
||||
export const TE_SYSEX_FILE_GET = 3;
|
||||
export const TE_SYSEX_FILE_GET_TYPE_INIT = 0;
|
||||
export const TE_SYSEX_FILE_GET_TYPE_DATA = 1;
|
||||
export const TE_SYSEX_FILE_LIST = 4;
|
||||
export const TE_SYSEX_FILE_METADATA = 7;
|
||||
export const TE_SYSEX_FILE_METADATA_GET = 2;
|
||||
export const TE_SYSEX_FILE_FILE_TYPE_FILE = 1;
|
||||
export const TE_SYSEX_FILE_INFO = 11;
|
||||
337
src/lib/midi/device.ts
Normal file
@@ -0,0 +1,337 @@
|
||||
import {
|
||||
BIT_IS_REQUEST,
|
||||
BIT_REQUEST_ID_AVAILABLE,
|
||||
IDENTITY_SYSEX,
|
||||
MIDI_SYSEX_END,
|
||||
MIDI_SYSEX_START,
|
||||
MIDI_SYSEX_TE,
|
||||
TE_MIDI_ID_0,
|
||||
TE_MIDI_ID_1,
|
||||
TE_MIDI_ID_2,
|
||||
TE_SYSEX,
|
||||
TE_SYSEX_GREET,
|
||||
} from './constants';
|
||||
import { TEDevice, TEDeviceIdentification, TESysexMessage } from './types';
|
||||
import {
|
||||
binToString,
|
||||
getNextRequestId,
|
||||
metadataStringToObject,
|
||||
packToBuffer,
|
||||
parseMidiIdentityResponse,
|
||||
sysexStatusToString,
|
||||
unpackInPlace,
|
||||
} from './utils';
|
||||
|
||||
let deviceInputPort: MIDIInput | null = null;
|
||||
let deviceOutputPort: MIDIOutput | null = null;
|
||||
let pendingInitialization = false;
|
||||
let deviceInitialized = false;
|
||||
const messageHandler = new Map<
|
||||
number,
|
||||
(inputPort: MIDIInput, requestId: number, data: Uint8Array) => void
|
||||
>();
|
||||
const portListeners = new Map<MIDIInput, (event: MIDIMessageEvent) => void>();
|
||||
|
||||
async function startEventListener(midiAccess: MIDIAccess) {
|
||||
const inputs = midiAccess.inputs.values();
|
||||
|
||||
// remove old listeners if any
|
||||
// mainly for hot reload during development
|
||||
stopEventListener();
|
||||
|
||||
const handleMidiMessage = (event: MIDIMessageEvent) => {
|
||||
// skip empty messages and non-sysex messages (like clock)
|
||||
if (!event.data || event.data.length === 0 || event.data[0] !== MIDI_SYSEX_START) {
|
||||
return;
|
||||
}
|
||||
|
||||
const inputPort = event.currentTarget as MIDIInput;
|
||||
|
||||
for (const [requestId, handler] of messageHandler.entries()) {
|
||||
handler(inputPort, requestId, event.data);
|
||||
}
|
||||
};
|
||||
|
||||
for (const inputPort of inputs) {
|
||||
inputPort.addEventListener('midimessage', handleMidiMessage);
|
||||
portListeners.set(inputPort, handleMidiMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function stopEventListener() {
|
||||
for (const [port, callback] of portListeners) {
|
||||
if (port) {
|
||||
port.removeEventListener('midimessage', callback);
|
||||
portListeners.delete(port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseTeenageSysex(bytes: Uint8Array) {
|
||||
const validHeader =
|
||||
bytes.length >= 9 &&
|
||||
bytes[0] === MIDI_SYSEX_START &&
|
||||
bytes[1] === TE_MIDI_ID_0 &&
|
||||
bytes[2] === TE_MIDI_ID_1 &&
|
||||
bytes[3] === TE_MIDI_ID_2 &&
|
||||
bytes[5] === MIDI_SYSEX_TE &&
|
||||
bytes[bytes.length - 1] === MIDI_SYSEX_END;
|
||||
|
||||
if (!validHeader) {
|
||||
return;
|
||||
}
|
||||
|
||||
const msg: TESysexMessage = {
|
||||
kind: 'te-sysex',
|
||||
identityCode: bytes[4],
|
||||
requestId: 0,
|
||||
hasRequestId: false,
|
||||
status: -1,
|
||||
hStatus: '',
|
||||
command: bytes[8],
|
||||
type: bytes[6] & BIT_IS_REQUEST ? 'request' : 'response',
|
||||
rawData: new Uint8Array(),
|
||||
hexData: '',
|
||||
hexCommand: '',
|
||||
string: '',
|
||||
};
|
||||
|
||||
if (bytes[6] & BIT_REQUEST_ID_AVAILABLE) {
|
||||
msg.hasRequestId = true;
|
||||
msg.requestId = ((bytes[6] & 0x1f) << 7) | (bytes[7] & 0x7f);
|
||||
}
|
||||
|
||||
let index = 9;
|
||||
|
||||
if (msg.type === 'response') {
|
||||
msg.status = bytes[index++];
|
||||
}
|
||||
|
||||
msg.hStatus = sysexStatusToString(msg.status);
|
||||
if (msg.hStatus === undefined) {
|
||||
console.error(`Cannot handle message with status ${msg.status}`);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.rawData = unpackInPlace(bytes.subarray(index, bytes.length - 1));
|
||||
msg.string = binToString(msg.rawData);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
async function sendIdentAndWaitForReponse(output: MIDIOutput, timeoutMs: number = 2_000) {
|
||||
return new Promise<{ inputPort: MIDIInput | null; data: Uint8Array } | null>((resolve) => {
|
||||
const requestId = 0;
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
messageHandler.delete(requestId);
|
||||
console.warn('Timeout waiting for identity response');
|
||||
resolve(null);
|
||||
}, timeoutMs);
|
||||
|
||||
const handleMidiMessage = (inputPort: MIDIInput, requestId: number, data: Uint8Array) => {
|
||||
if (requestId === 0) {
|
||||
clearTimeout(timeoutId);
|
||||
messageHandler.delete(requestId);
|
||||
resolve({ inputPort, data });
|
||||
}
|
||||
};
|
||||
|
||||
messageHandler.set(requestId, handleMidiMessage);
|
||||
|
||||
output.send(IDENTITY_SYSEX);
|
||||
});
|
||||
}
|
||||
|
||||
export async function discoverDevicePorts(
|
||||
midiAccess: MIDIAccess,
|
||||
): Promise<TEDeviceIdentification | null> {
|
||||
const outputs = Array.from(midiAccess.outputs.values());
|
||||
let parsedResponse: TEDeviceIdentification | null = null;
|
||||
|
||||
if (outputs.length === 0) {
|
||||
return parsedResponse;
|
||||
}
|
||||
|
||||
for (const output of outputs) {
|
||||
try {
|
||||
console.group('Trying output port:', output.name);
|
||||
|
||||
const response = await sendIdentAndWaitForReponse(output);
|
||||
if (response) {
|
||||
console.debug('Checking response for output port:', output.name);
|
||||
|
||||
parsedResponse = parseMidiIdentityResponse(response.data);
|
||||
if (parsedResponse) {
|
||||
console.log('Found TE device on port:', output.name);
|
||||
|
||||
setDeviceOutputPort(output);
|
||||
setDeviceInputPort(response.inputPort);
|
||||
// setDeviceInputPort(
|
||||
// midiAccess.inputs.values().find((inp) => inp.name?.includes('EP-13')) || null, // handle both EP-133 and EP-1320
|
||||
// );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return parsedResponse;
|
||||
}
|
||||
|
||||
function sendTESysEx(
|
||||
midiOutput: MIDIOutput,
|
||||
identityCode: number,
|
||||
command: number,
|
||||
data: Uint8Array,
|
||||
) {
|
||||
const packedLength = data.length > 0 ? data.length + Math.ceil(data.length / 7) : 0;
|
||||
const message = new Uint8Array(10 + packedLength);
|
||||
const requestId = getNextRequestId(midiOutput.id);
|
||||
|
||||
const header = new Uint8Array([
|
||||
MIDI_SYSEX_START,
|
||||
TE_MIDI_ID_0,
|
||||
TE_MIDI_ID_1,
|
||||
TE_MIDI_ID_2,
|
||||
identityCode,
|
||||
MIDI_SYSEX_TE,
|
||||
BIT_IS_REQUEST | BIT_REQUEST_ID_AVAILABLE | ((requestId >> 7) & 0x1f),
|
||||
requestId & 0x7f,
|
||||
command,
|
||||
]);
|
||||
|
||||
message.set(header, 0);
|
||||
message[message.length - 1] = MIDI_SYSEX_END;
|
||||
|
||||
packToBuffer(data, message.subarray(9, 9 + packedLength));
|
||||
|
||||
midiOutput.send(message);
|
||||
|
||||
return requestId;
|
||||
}
|
||||
|
||||
export async function sendSysexToDevice(
|
||||
command: number,
|
||||
payload: Uint8Array | Array<number> = [],
|
||||
timeoutMs: number = 5_000,
|
||||
): Promise<TESysexMessage | null> {
|
||||
return new Promise<TESysexMessage | null>((resolve, reject) => {
|
||||
if (!deviceOutputPort || !deviceInputPort) {
|
||||
reject('No device output port available');
|
||||
return;
|
||||
}
|
||||
|
||||
const currentRequestId = sendTESysEx(deviceOutputPort, 0, command, new Uint8Array(payload));
|
||||
|
||||
const timeoutHandler = () => {
|
||||
messageHandler.delete(currentRequestId);
|
||||
reject(`Timeout waiting for sysex response for request ${currentRequestId}`);
|
||||
};
|
||||
|
||||
const timeoutId = setTimeout(timeoutHandler, timeoutMs);
|
||||
|
||||
const handleMidiMessage = (_inputPort: MIDIInput, requestId: number, data: Uint8Array) => {
|
||||
if (requestId !== currentRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const response = parseTeenageSysex(data);
|
||||
if (!response || response.type !== 'response' || response.requestId !== currentRequestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
messageHandler.delete(currentRequestId);
|
||||
|
||||
if (response.status === TE_SYSEX.STATUS_OK) {
|
||||
resolve(response);
|
||||
} else if (response.status === TE_SYSEX.STATUS_SPECIFIC_SUCCESS_START) {
|
||||
reject('Partial response handling not implemented yet');
|
||||
} else {
|
||||
reject(`Received error status in sysex response: ${JSON.stringify(response)}`);
|
||||
}
|
||||
};
|
||||
|
||||
messageHandler.set(currentRequestId, handleMidiMessage);
|
||||
});
|
||||
}
|
||||
|
||||
export async function tryInitDevice(
|
||||
midiAccess: MIDIAccess,
|
||||
onDeviceFound?: (deviceInfo: TEDevice) => void,
|
||||
) {
|
||||
if (pendingInitialization || deviceInitialized) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
pendingInitialization = true;
|
||||
|
||||
await startEventListener(midiAccess);
|
||||
|
||||
const deviceIdentification = await discoverDevicePorts(midiAccess);
|
||||
if (!deviceIdentification) {
|
||||
return;
|
||||
}
|
||||
|
||||
const greetResponse = await sendSysexToDevice(TE_SYSEX_GREET);
|
||||
if (!greetResponse) {
|
||||
throw new Error('No greetings from device');
|
||||
}
|
||||
|
||||
const deviceMetadata = metadataStringToObject(greetResponse.string);
|
||||
const deviceInfo: TEDevice = {
|
||||
inputId: getDeviceInputPort()?.id || '',
|
||||
outputId: getDeviceOutputPort()?.id || '',
|
||||
sku: deviceMetadata.sku,
|
||||
serial: deviceMetadata.serial,
|
||||
metadata: deviceMetadata,
|
||||
};
|
||||
|
||||
onDeviceFound?.(deviceInfo);
|
||||
|
||||
deviceInitialized = true;
|
||||
|
||||
return deviceMetadata;
|
||||
} catch (error) {
|
||||
console.error('Error accessing MIDI:', error);
|
||||
|
||||
return null;
|
||||
} finally {
|
||||
pendingInitialization = false;
|
||||
}
|
||||
}
|
||||
|
||||
export function getDeviceInputPort() {
|
||||
return deviceInputPort;
|
||||
}
|
||||
|
||||
export function getDeviceOutputPort() {
|
||||
return deviceOutputPort;
|
||||
}
|
||||
|
||||
export function setDeviceInputPort(port: MIDIInput | null) {
|
||||
deviceInputPort = port;
|
||||
}
|
||||
|
||||
export function setDeviceOutputPort(port: MIDIOutput | null) {
|
||||
deviceOutputPort = port;
|
||||
}
|
||||
|
||||
export function disconnectDevice() {
|
||||
stopEventListener();
|
||||
setDeviceInputPort(null);
|
||||
setDeviceOutputPort(null);
|
||||
deviceInitialized = false;
|
||||
}
|
||||
|
||||
export function canInitializeDevice() {
|
||||
return (
|
||||
!pendingInitialization && !deviceInitialized && getDeviceInputPort() && getDeviceOutputPort()
|
||||
);
|
||||
}
|
||||
195
src/lib/midi/fs.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
import { TE_SYSEX_FILE, TE_SYSEX_FILE_FILE_TYPE_FILE } from './constants';
|
||||
import { sendSysexToDevice } from './device';
|
||||
import {
|
||||
buildSysExFileGetMetadataRequest,
|
||||
buildSysExFileInitRequest,
|
||||
buildSysExFileListRequest,
|
||||
buildSysExGetFileDataRequest,
|
||||
buildSysExGetFileInitRequest,
|
||||
parseGetMetadataResponse,
|
||||
parseSysExFileListResponse,
|
||||
parseSysExGetFileDataResponse,
|
||||
parseSysexGetFileInitResponse,
|
||||
} from './fsSysex';
|
||||
import { TEFile, TEFileNode } from './types';
|
||||
import { crc32, sanitizeBrokenJson } from './utils';
|
||||
|
||||
const fileNodesCache: Record<string, TEFileNode> = {};
|
||||
|
||||
export async function getFile(
|
||||
nodeId: number,
|
||||
progressCallback?: (bytesRead: number, totalBytes: number) => void,
|
||||
): Promise<TEFile> {
|
||||
const offset = 0;
|
||||
const options = null;
|
||||
const chunks = [];
|
||||
let bytesRead = 0;
|
||||
let pageIndex = 0;
|
||||
|
||||
const initResponseRaw = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExGetFileInitRequest(nodeId, offset, options),
|
||||
);
|
||||
|
||||
if (!initResponseRaw) {
|
||||
throw new Error('Failed to get file init response from device');
|
||||
}
|
||||
|
||||
const initResponse = parseSysexGetFileInitResponse(initResponseRaw.rawData);
|
||||
const totalRemaining = initResponse.fileSize - offset;
|
||||
|
||||
while (bytesRead < totalRemaining) {
|
||||
const chunkResponseRaw = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExGetFileDataRequest(pageIndex),
|
||||
);
|
||||
|
||||
if (!chunkResponseRaw) {
|
||||
throw new Error('Failed to get file data response from device');
|
||||
}
|
||||
|
||||
const chunkResponse = parseSysExGetFileDataResponse(chunkResponseRaw.rawData);
|
||||
if (chunkResponse.page !== pageIndex) {
|
||||
throw new Error(`Unexpected page number ${chunkResponse.page}, expected ${pageIndex}`);
|
||||
}
|
||||
|
||||
if (chunkResponse.data.byteLength === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
chunks.push(chunkResponse.data);
|
||||
|
||||
bytesRead += chunkResponse.data.byteLength;
|
||||
progressCallback?.(bytesRead, totalRemaining);
|
||||
pageIndex = chunkResponse.nextPage;
|
||||
}
|
||||
|
||||
const length = chunks.reduce((acc, buf) => acc + buf.length, 0);
|
||||
const combined = new Uint8Array(length);
|
||||
let bufOffset = 0;
|
||||
|
||||
for (const buf of chunks) {
|
||||
combined.set(buf, bufOffset);
|
||||
bufOffset += buf.length;
|
||||
}
|
||||
|
||||
return {
|
||||
name: initResponse.fileName,
|
||||
size: initResponse.fileSize,
|
||||
data: combined,
|
||||
crc32: crc32(combined),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getFileList(): Promise<TEFileNode[]> {
|
||||
await sendSysexToDevice(TE_SYSEX_FILE, buildSysExFileInitRequest(4 * 1024 * 1024, 0));
|
||||
|
||||
return getFileListInternal();
|
||||
}
|
||||
|
||||
async function getFileListInternal(
|
||||
nodeId: number = 0,
|
||||
filesList: TEFileNode[] = [],
|
||||
path = '/',
|
||||
): Promise<TEFileNode[]> {
|
||||
let page = 0;
|
||||
|
||||
while (true) {
|
||||
const fileListResponse = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExFileListRequest(page, nodeId),
|
||||
);
|
||||
|
||||
if (!fileListResponse || !fileListResponse.rawData || fileListResponse.rawData.length < 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
const pageNumber = ((fileListResponse.rawData[0] << 8) | fileListResponse.rawData[1]) & 0xffff;
|
||||
if (pageNumber !== page) {
|
||||
throw new Error(`Unexpected page ${pageNumber}, expected ${page}`);
|
||||
}
|
||||
|
||||
page += 1;
|
||||
|
||||
const payload = fileListResponse?.rawData.slice(2);
|
||||
const list = parseSysExFileListResponse(payload);
|
||||
if (list.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (const entry of list) {
|
||||
const filePath = path === '/' ? `${path}${entry.fileName}` : `${path}/${entry.fileName}`;
|
||||
const fileType = entry.flags & TE_SYSEX_FILE_FILE_TYPE_FILE ? 'file' : 'folder';
|
||||
|
||||
const item = {
|
||||
...entry,
|
||||
fileName: filePath,
|
||||
fileType,
|
||||
} as const;
|
||||
|
||||
fileNodesCache[filePath] = item;
|
||||
filesList.push(item);
|
||||
|
||||
if (fileType === 'folder') {
|
||||
await getFileListInternal(entry.nodeId, filesList, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filesList;
|
||||
}
|
||||
|
||||
export async function getFileMetadata<T extends Record<string, any>>(nodeId: number, pages = null) {
|
||||
const result: T = {} as T;
|
||||
|
||||
for (const pageSelector of pages || [null]) {
|
||||
let metadataStr = '';
|
||||
let page = 0;
|
||||
|
||||
while (true) {
|
||||
const response = await sendSysexToDevice(
|
||||
TE_SYSEX_FILE,
|
||||
buildSysExFileGetMetadataRequest(nodeId, page, pageSelector),
|
||||
);
|
||||
|
||||
if (!response || response?.rawData.byteLength <= 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
const parsed = parseGetMetadataResponse(response.rawData);
|
||||
if (parsed.page !== page) {
|
||||
throw new Error(`unexpected page ${parsed.page}, expected ${page}`);
|
||||
}
|
||||
|
||||
page += 1;
|
||||
metadataStr += parsed.metadata;
|
||||
|
||||
if (response.rawData.slice(-1)[0] === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(result, JSON.parse(sanitizeBrokenJson(metadataStr)));
|
||||
} catch (err) {
|
||||
throw new Error(`Could not parse ${metadataStr}: ${(err as Error).message}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getFileNodeByPath(path: string) {
|
||||
if (fileNodesCache[path]) {
|
||||
return fileNodesCache[path];
|
||||
}
|
||||
|
||||
const filesList = await getFileList();
|
||||
|
||||
return filesList.find((file) => file.fileName === path) || null;
|
||||
}
|
||||
|
||||
export function resetFileCache() {
|
||||
for (const key in fileNodesCache) {
|
||||
delete fileNodesCache[key];
|
||||
}
|
||||
}
|
||||
172
src/lib/midi/fsSysex.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import {
|
||||
TE_SYSEX_FILE_GET,
|
||||
TE_SYSEX_FILE_GET_TYPE_DATA,
|
||||
TE_SYSEX_FILE_GET_TYPE_INIT,
|
||||
TE_SYSEX_FILE_INFO,
|
||||
TE_SYSEX_FILE_INIT,
|
||||
TE_SYSEX_FILE_LIST,
|
||||
TE_SYSEX_FILE_METADATA,
|
||||
TE_SYSEX_FILE_METADATA_GET,
|
||||
} from './constants';
|
||||
import { parseNullTerminatedString, writeStringToView } from './utils';
|
||||
|
||||
export function buildSysExFileInitRequest(maxResponseLength: number, flags: number) {
|
||||
const buffer = new Uint8Array(6);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_INIT);
|
||||
view.setUint8(1, flags);
|
||||
view.setUint32(2, maxResponseLength);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function buildSysExFileInfoRequest(fileId: number) {
|
||||
const buffer = new Uint8Array(3);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_INFO);
|
||||
view.setUint16(1, fileId);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseFileInfoResponse(data: Uint8Array) {
|
||||
const nodeId = (data[0] << 8) | data[1];
|
||||
const parentId = (data[2] << 8) | data[3];
|
||||
const flags = data[4];
|
||||
const fileSize = (data[5] << 24) | (data[6] << 16) | (data[7] << 8) | data[8];
|
||||
const fileName = parseNullTerminatedString(data, 9);
|
||||
|
||||
return {
|
||||
nodeId,
|
||||
parentId,
|
||||
flags,
|
||||
fileSize,
|
||||
fileName,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSysExFileListRequest(page: number, nodeId: number) {
|
||||
const buffer = new Uint8Array(5);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_LIST);
|
||||
view.setUint16(1, page);
|
||||
view.setUint16(3, nodeId);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseSysExFileListResponse(data: Uint8Array) {
|
||||
const entries = [];
|
||||
let offset = 0;
|
||||
|
||||
while (offset < data.byteLength) {
|
||||
const nodeId = (data[offset] << 8) | data[offset + 1];
|
||||
const flags = data[offset + 2];
|
||||
const fileSize =
|
||||
(data[offset + 3] << 24) |
|
||||
(data[offset + 4] << 16) |
|
||||
(data[offset + 5] << 8) |
|
||||
data[offset + 6];
|
||||
const fileName = parseNullTerminatedString(data, offset + 7);
|
||||
const length = 7 + fileName.length;
|
||||
|
||||
entries.push({
|
||||
nodeId,
|
||||
flags,
|
||||
fileSize,
|
||||
fileName,
|
||||
});
|
||||
|
||||
offset += length + 1;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export function buildSysExGetFileInitRequest(
|
||||
fileId: number,
|
||||
offset: number,
|
||||
extraArgs: Uint8Array | null = null,
|
||||
) {
|
||||
const length = extraArgs ? 16 + extraArgs.length : 8;
|
||||
const buffer = new Uint8Array(length);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_GET);
|
||||
view.setUint8(1, TE_SYSEX_FILE_GET_TYPE_INIT);
|
||||
view.setUint16(2, fileId);
|
||||
view.setUint32(4, offset);
|
||||
|
||||
if (extraArgs != null) {
|
||||
view.setBigUint64(8, 0n);
|
||||
for (let i = 0; i < extraArgs.length; i++) {
|
||||
view.setUint8(16 + i, extraArgs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseSysexGetFileInitResponse(bytes: Uint8Array) {
|
||||
const fileId = (bytes[0] << 8) | bytes[1];
|
||||
const flags = bytes[2];
|
||||
const fileSize = (bytes[3] << 24) | (bytes[4] << 16) | (bytes[5] << 8) | bytes[6];
|
||||
const fileName = parseNullTerminatedString(bytes, 7);
|
||||
|
||||
return {
|
||||
fileId,
|
||||
flags,
|
||||
fileSize,
|
||||
fileName,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSysExGetFileDataRequest(page: number) {
|
||||
const buffer = new Uint8Array(4);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_GET);
|
||||
view.setUint8(1, TE_SYSEX_FILE_GET_TYPE_DATA);
|
||||
view.setUint16(2, page);
|
||||
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
export function parseSysExGetFileDataResponse(bytes: Uint8Array) {
|
||||
return {
|
||||
page: (bytes[0] << 8) | bytes[1],
|
||||
nextPage: ((bytes[0] << 8) | (bytes[1] + 1)) & 0xffff,
|
||||
data: bytes.subarray(2),
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSysExFileGetMetadataRequest(
|
||||
fileId: number,
|
||||
page: number,
|
||||
key: string | null = null,
|
||||
) {
|
||||
const extraLength = key?.length ? key.length + 1 : 0;
|
||||
const buffer = new Uint8Array(6 + extraLength);
|
||||
const view = new DataView(buffer.buffer);
|
||||
|
||||
view.setUint8(0, TE_SYSEX_FILE_METADATA);
|
||||
view.setUint8(1, TE_SYSEX_FILE_METADATA_GET);
|
||||
view.setUint16(2, fileId);
|
||||
view.setUint16(4, page);
|
||||
|
||||
if (key != null) {
|
||||
writeStringToView(view, 6, key, true);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
export function parseGetMetadataResponse(data: Uint8Array) {
|
||||
const page = (data[0] << 8) | data[1];
|
||||
const metadata = parseNullTerminatedString(data, 2);
|
||||
|
||||
return { page, metadata };
|
||||
}
|
||||
64
src/lib/midi/index.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { disconnectDevice, getDeviceInputPort, getDeviceOutputPort, tryInitDevice } from './device';
|
||||
import { getFile, getFileNodeByPath, resetFileCache } from './fs';
|
||||
import { TEDevice } from './types';
|
||||
|
||||
export async function initDevice({
|
||||
onDeviceFound,
|
||||
onDeviceLost,
|
||||
onNoMidiAccess,
|
||||
}: {
|
||||
onDeviceFound?: (deviceInfo: TEDevice) => void;
|
||||
onDeviceLost?: () => void;
|
||||
onNoMidiAccess?: (error: Error) => void;
|
||||
}): Promise<void> {
|
||||
let midiAccess: MIDIAccess | null = null;
|
||||
|
||||
try {
|
||||
midiAccess = await navigator.requestMIDIAccess({ sysex: true });
|
||||
} catch (error) {
|
||||
onNoMidiAccess?.(error as Error);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await tryInitDevice(midiAccess, onDeviceFound);
|
||||
|
||||
const stateChangeMidiEventHandler = (event: MIDIConnectionEvent) => {
|
||||
const currentInputPort = getDeviceInputPort();
|
||||
const currentOutputPort = getDeviceOutputPort();
|
||||
|
||||
if (
|
||||
event.port?.state === 'disconnected' &&
|
||||
(currentInputPort?.id === event.port?.id || currentOutputPort?.id === event.port?.id)
|
||||
) {
|
||||
disconnectDevice();
|
||||
resetFileCache();
|
||||
onDeviceLost?.();
|
||||
}
|
||||
|
||||
if (event.port?.state === 'connected') {
|
||||
setTimeout(() => tryInitDevice(midiAccess, onDeviceFound), 1000);
|
||||
}
|
||||
};
|
||||
|
||||
midiAccess.addEventListener('statechange', stateChangeMidiEventHandler);
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.on('vite:beforeUpdate', () => {
|
||||
midiAccess?.removeEventListener('statechange', stateChangeMidiEventHandler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProjectFile(projectId: number) {
|
||||
const path = `/projects/${String(projectId).padStart(2, '0')}`;
|
||||
const foundNode = await getFileNodeByPath(path);
|
||||
|
||||
if (!foundNode) {
|
||||
throw new Error(`Project ${projectId} not found`);
|
||||
}
|
||||
|
||||
const archive = await getFile(foundNode.nodeId);
|
||||
|
||||
return archive;
|
||||
}
|
||||