├── src ├── vite-env.d.ts ├── ui │ ├── theming.ts │ ├── styles.css │ ├── playback.ts │ ├── components │ │ ├── BrowseButton.tsx │ │ ├── KeyScaleSelection.tsx │ │ ├── SettingsModalContent.tsx │ │ └── SampleListEntry.tsx │ └── App.tsx ├── splice │ ├── entities.ts │ ├── decoder.ts │ └── api.ts ├── main.tsx ├── native.ts └── config.ts ├── src-tauri ├── build.rs ├── icons │ ├── 32x32.png │ ├── icon.ico │ ├── icon.png │ ├── 128x128.png │ └── 128x128@2x.png ├── .gitignore ├── src │ ├── main.rs │ └── files.rs ├── Cargo.toml ├── tauri.conf.json └── Cargo.lock ├── .gitattributes ├── etc ├── screenshot.png ├── vid-demo-basic.mp4 └── vid-demo-drumloop.mp4 ├── public └── img │ ├── blob-heart.png │ ├── blob-think.png │ ├── bg-gradient.png │ ├── blob-salute.png │ └── missing-cover.png ├── .vscode └── extensions.json ├── postcss.config.js ├── tsconfig.node.json ├── index.html ├── .gitignore ├── tailwind.config.js ├── tsconfig.json ├── vite.config.ts ├── README.md ├── package.json └── LICENSE /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /etc/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/etc/screenshot.png -------------------------------------------------------------------------------- /etc/vid-demo-basic.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/etc/vid-demo-basic.mp4 -------------------------------------------------------------------------------- /etc/vid-demo-drumloop.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/etc/vid-demo-drumloop.mp4 -------------------------------------------------------------------------------- /public/img/blob-heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/public/img/blob-heart.png -------------------------------------------------------------------------------- /public/img/blob-think.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/public/img/blob-think.png -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /public/img/bg-gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/public/img/bg-gradient.png -------------------------------------------------------------------------------- /public/img/blob-salute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/public/img/blob-salute.png -------------------------------------------------------------------------------- /public/img/missing-cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/public/img/missing-cover.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ascpixi/splicedd/HEAD/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] 3 | } 4 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /src/ui/theming.ts: -------------------------------------------------------------------------------- 1 | import { cfg } from "../config"; 2 | 3 | export function refreshDarkMode() { 4 | if (cfg().darkMode) { 5 | document.documentElement.classList.add("dark"); 6 | } else { 7 | document.documentElement.classList.remove("dark"); 8 | } 9 | } -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/ui/styles.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html, body { 6 | width: 100%; 7 | height: 100%; 8 | margin: 0; 9 | padding: 0; 10 | overflow: hidden; 11 | } 12 | 13 | body { 14 | background-image: url("img/bg-gradient.png"); 15 | background-position: center; 16 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/splice/entities.ts: -------------------------------------------------------------------------------- 1 | export type SpliceSortBy = "relevance" | "popularity" | "recency" | "random"; 2 | 3 | export type SpliceSampleType = "oneshot" | "loop"; 4 | 5 | export type SpliceTag = { 6 | uuid: string; 7 | label: string; 8 | } 9 | 10 | export type MusicKey = "C" | "C#" | "D" | "D#" | "E" | "F" | "F#" | "G" | "G#" | "A" | "A#" | "B"; 11 | 12 | export type ChordType = "major" | "minor"; 13 | 14 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | const { nextui } = require("@nextui-org/react"); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: [ 6 | "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}", 7 | "./src/**/*.{js,ts,jsx,tsx}", 8 | "./index.html" 9 | ], 10 | theme: { 11 | extend: {}, 12 | }, 13 | darkMode: "class", 14 | plugins: [nextui()] 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/ui/playback.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | /** 4 | * Represents a shared audio context. 5 | */ 6 | export interface SamplePlaybackContext { 7 | cancellation: SamplePlaybackCancellation | null; 8 | setCancellation: React.Dispatch>; 9 | } 10 | 11 | /** 12 | * Represents a function that can signal to the current owner of a given context to 13 | * give up control over said context. 14 | */ 15 | export type SamplePlaybackCancellation = () => void; -------------------------------------------------------------------------------- /src-tauri/src/main.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Prevents additional console window on Windows in release, DO NOT REMOVE!! 4 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 5 | 6 | mod files; 7 | 8 | fn main() { 9 | tauri::Builder::default() 10 | .plugin(tauri_plugin_drag::init()) 11 | .invoke_handler(tauri::generate_handler![ 12 | files::write_sample_file, 13 | files::file_exists, 14 | files::create_placeholder_file 15 | ]) 16 | .run(tauri::generate_context!()) 17 | .expect("error while running tauri application"); 18 | } 19 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { NextUIProvider } from "@nextui-org/react"; 4 | import { Buffer } from "buffer"; 5 | 6 | import App from "./ui/App"; 7 | import { loadConfig } from "./config"; 8 | import { refreshDarkMode } from "./ui/theming"; 9 | 10 | import "./ui/styles.css"; 11 | 12 | window.Buffer = Buffer; // required for node-wav 13 | 14 | await loadConfig(); 15 | 16 | refreshDarkMode(); 17 | 18 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 19 | 20 | 21 | 22 | 23 | , 24 | ); 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /src/ui/components/BrowseButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button, ButtonProps } from "@nextui-org/react"; 2 | import { open } from "@tauri-apps/api/dialog"; 3 | 4 | /** 5 | * Renders a button which, when clicked, will prompt the user to select a file-system entry. 6 | */ 7 | export default function BrowseButton(props: ButtonProps & { 8 | onPick: (path: string) => void, 9 | directory?: boolean, 10 | title?: string 11 | }) { 12 | async function handleClick() { 13 | const path = await open({ 14 | directory: props.directory, 15 | title: props.title 16 | }); 17 | 18 | if (typeof path == "string") { 19 | props.onPick(path); 20 | } 21 | } 22 | 23 | return 24 | } -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig(async () => ({ 6 | plugins: [react()], 7 | 8 | esbuild: { 9 | supported: { 10 | "top-level-await": true 11 | } 12 | }, 13 | 14 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 15 | // 16 | // 1. prevent vite from obscuring rust errors 17 | clearScreen: false, 18 | // 2. tauri expects a fixed port, fail if that port is not available 19 | server: { 20 | port: 1420, 21 | strictPort: true, 22 | watch: { 23 | // 3. tell vite to ignore watching `src-tauri` 24 | ignored: ["**/src-tauri/**"], 25 | }, 26 | }, 27 | })); 28 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "splicedd" 3 | version = "1.1.1" 4 | description = "A custom frontend for the Splice sample library" 5 | authors = ["ascpixi"] 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [build-dependencies] 11 | tauri-build = { version = "1", features = [] } 12 | 13 | [dependencies] 14 | tauri = { version = "1", features = [ "path-all", "dialog-open", "http-all", "fs-all", "protocol-all", "shell-open"] } 15 | serde = { version = "1", features = ["derive"] } 16 | serde_json = "1" 17 | tauri-plugin-drag = "0.3.1" 18 | 19 | [features] 20 | # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! 21 | custom-protocol = ["tauri/custom-protocol"] 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # splicedd 2 | **Splicedd** is an alternative frontend for the popular [Splice](https://splice.com/features/sounds) sample library. It does not require any kind of authentication, and contains all of the most important features of the regular desktop app (including drag-and-drop). 3 | 4 |

5 | click here to download the latest release! 6 |

7 | 8 |

9 | 10 | Basic demo | Drum loop demo 11 | ------------|--------------- 12 |