Add boilerplate files and licenses

This commit is contained in:
2026-07-13 04:39:10 +02:00
parent 69cabda489
commit fac7e80bb7
202 changed files with 23125 additions and 0 deletions

42
src/App.tsx Normal file
View 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
View File

@@ -0,0 +1,4 @@
import { atom } from 'jotai';
import { SKU_EP133 } from '~/lib/constants';
export const deviceSkuAtom = atom<string>(SKU_EP133);

View 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[]>([]);

View File

@@ -0,0 +1,3 @@
import { atom } from 'jotai';
export const feedbackDialogAtom = atom<boolean>(false);

4
src/atoms/project.ts Normal file
View 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
View File

@@ -0,0 +1,3 @@
import { atom } from 'jotai';
export const swUpdateAvailableAtom = atom(false);

19
src/atoms/toast.ts Normal file
View 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),
);
});

View 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;

View 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;

View 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;

View 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
View 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;

View 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,
)}
>
Youre currently offline, but the app is still working just fine!
</div>
);
}
export default OfflineInformer;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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;

View 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;

View 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;

View 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;

View 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;

View 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;

View 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
View 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]);
}

View 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
View 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
View 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;

View 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;

View 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]);
}

View 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
View 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;

View 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
View 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;

View 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
View 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
View 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
View 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'];

View 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;
}

View 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;

View 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>

View 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>

View 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&amp;%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>

View 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>

View 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&amp;%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>

View 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&amp;%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>

View 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&amp;%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>

View 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&amp;%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>

View 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>

View 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>

View 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>

File diff suppressed because it is too large Load Diff

View 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>

View 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>

File diff suppressed because it is too large Load Diff

View 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>

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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);
}

View 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
View 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;

View 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;

View 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();
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

Some files were not shown because too many files have changed in this diff Show More