Merge remote-tracking branch 'origin/main' into ladder_filter
This commit is contained in:
commit
888e0d4751
38 changed files with 2163 additions and 34 deletions
3
website/.gitignore
vendored
3
website/.gitignore
vendored
|
|
@ -17,3 +17,6 @@ pnpm-debug.log*
|
|||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
public/tree-sitter.wasm
|
||||
public/tree-sitter-haskell.wasm
|
||||
|
|
@ -9,7 +9,8 @@
|
|||
"check": "astro check && tsc",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview --port 3009 --host 0.0.0.0",
|
||||
"astro": "astro"
|
||||
"astro": "astro",
|
||||
"postinstall": "cp node_modules/hs2js/dist/tree-sitter.wasm public && cp node_modules/hs2js/dist/tree-sitter-haskell.wasm public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/client-search": "^4.22.0",
|
||||
|
|
@ -39,6 +40,7 @@
|
|||
"@strudel/transpiler": "workspace:*",
|
||||
"@strudel/webaudio": "workspace:*",
|
||||
"@strudel/xen": "workspace:*",
|
||||
"@strudel/tidal": "workspace:*",
|
||||
"@supabase/supabase-js": "^2.39.1",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
|
|
@ -49,6 +51,7 @@
|
|||
"astro": "^4.0.8",
|
||||
"claviature": "^0.1.0",
|
||||
"date-fns": "^3.2.0",
|
||||
"hs2js": "0.0.8",
|
||||
"nanoid": "^5.0.4",
|
||||
"nanostores": "^0.9.5",
|
||||
"react": "^18.2.0",
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
|
|||
sound("<bd rim>").bank("RolandTR707").delay(.5),
|
||||
n("<4 [3@3 4] [<2 0> ~@16] ~>")
|
||||
.scale("D4:minor").sound("gm_accordion:2")
|
||||
room(2).gain(.5)
|
||||
.room(2).gain(.5)
|
||||
)`}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import { getRandomTune, initCode, loadModules, shareCode, ReplContext } from './
|
|||
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
|
||||
import './Repl.css';
|
||||
import { setInterval, clearInterval } from 'worker-timers';
|
||||
import { getMetadata } from '../metadata_parser';
|
||||
|
||||
const { latestCode } = settingsMap.get();
|
||||
|
||||
|
|
@ -91,6 +92,7 @@ export function Repl({ embedded = false }) {
|
|||
const { code } = all;
|
||||
setLatestCode(code);
|
||||
window.location.hash = '#' + code2hash(code);
|
||||
setDocumentTitle(code);
|
||||
const viewingPatternData = getViewingPatternData();
|
||||
const data = { ...viewingPatternData, code };
|
||||
let id = data.id;
|
||||
|
|
@ -116,18 +118,20 @@ export function Repl({ embedded = false }) {
|
|||
// init settings
|
||||
|
||||
initCode().then(async (decoded) => {
|
||||
let msg;
|
||||
let code, msg;
|
||||
if (decoded) {
|
||||
editor.setCode(decoded);
|
||||
code = decoded;
|
||||
msg = `I have loaded the code from the URL.`;
|
||||
} else if (latestCode) {
|
||||
editor.setCode(latestCode);
|
||||
code = latestCode;
|
||||
msg = `Your last session has been loaded!`;
|
||||
} else {
|
||||
const { code: randomTune, name } = await getRandomTune();
|
||||
editor.setCode(randomTune);
|
||||
code = randomTune;
|
||||
msg = `A random code snippet named "${name}" has been loaded!`;
|
||||
}
|
||||
editor.setCode(code);
|
||||
setDocumentTitle(code);
|
||||
logger(`Welcome to Strudel! ${msg} Press play or hit ctrl+enter to run it!`, 'highlight');
|
||||
});
|
||||
|
||||
|
|
@ -170,6 +174,11 @@ export function Repl({ embedded = false }) {
|
|||
// UI Actions
|
||||
//
|
||||
|
||||
const setDocumentTitle = (code) => {
|
||||
const meta = getMetadata(code);
|
||||
document.title = (meta.title ? `${meta.title} - ` : '') + 'Strudel REPL';
|
||||
};
|
||||
|
||||
const handleTogglePlay = async () => {
|
||||
editorRef.current?.toggle();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@ export const registerSamplesFromDB = (config = userSamplesDBConfig, onComplete =
|
|||
if (!isAudioFile(title)) {
|
||||
return;
|
||||
}
|
||||
const splitRelativePath = soundFile.id?.split('/');
|
||||
const parentDirectory = splitRelativePath[splitRelativePath.length - 2];
|
||||
const splitRelativePath = soundFile.id.split('/');
|
||||
const parentDirectory =
|
||||
//fallback to file name before period and seperator if no parent directory
|
||||
splitRelativePath[splitRelativePath.length - 2] ?? soundFile.id.split(/\W+/)[0] ?? 'user';
|
||||
const soundPath = soundFile.blob;
|
||||
const soundPaths = sounds.get(parentDirectory) ?? new Set();
|
||||
soundPaths.add(soundPath);
|
||||
|
|
@ -114,6 +116,7 @@ const processFilesForIDB = async (files) => {
|
|||
Array.from(files)
|
||||
.map(async (s) => {
|
||||
const title = s.name;
|
||||
|
||||
if (!isAudioFile(title)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -121,12 +124,14 @@ const processFilesForIDB = async (files) => {
|
|||
const sUrl = URL.createObjectURL(s);
|
||||
//fetch the sound and turn it into a buffer array
|
||||
const buf = await fetch(sUrl).then((res) => res.arrayBuffer());
|
||||
//create a url blob containing all of the buffer data
|
||||
//create a url base64 containing all of the buffer data
|
||||
const base64 = await bufferToDataUrl(buf);
|
||||
const path = s.webkitRelativePath;
|
||||
const id = path?.length ? path : title;
|
||||
return {
|
||||
title,
|
||||
blob: base64,
|
||||
id: s.webkitRelativePath,
|
||||
id,
|
||||
};
|
||||
})
|
||||
.filter(Boolean),
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export default function ImportSoundsButton({ onComplete }) {
|
|||
return;
|
||||
}
|
||||
setIsUploading(true);
|
||||
|
||||
await uploadSamplesToDB(userSamplesDBConfig, fileUploadRef.current.files).then(() => {
|
||||
registerSamplesFromDB(userSamplesDBConfig, () => {
|
||||
onComplete();
|
||||
|
|
@ -32,7 +33,7 @@ export default function ImportSoundsButton({ onComplete }) {
|
|||
directory=""
|
||||
webkitdirectory=""
|
||||
multiple
|
||||
accept="audio/*"
|
||||
accept="audio/*, .wav, .mp3, .m4a, .flac"
|
||||
onChange={() => {
|
||||
onChange();
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ export function SoundsTab() {
|
|||
const sounds = useStore(soundMap);
|
||||
const { soundsFilter } = useSettings();
|
||||
const soundEntries = useMemo(() => {
|
||||
let filtered = Object.entries(sounds).filter(([key]) => !key.startsWith('_'));
|
||||
let filtered = Object.entries(sounds)
|
||||
.filter(([key]) => !key.startsWith('_'))
|
||||
.sort((a, b) => a[0].localeCompare(b[0]));
|
||||
if (!sounds) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -43,7 +45,7 @@ export function SoundsTab() {
|
|||
});
|
||||
return (
|
||||
<div id="sounds-tab" className="px-4 flex flex-col w-full h-full dark:text-white text-stone-900">
|
||||
<div className="pb-2 flex shrink-0 overflow-auto">
|
||||
<div className="pb-2 flex shrink-0 flex-wrap">
|
||||
<ButtonGroup
|
||||
value={soundsFilter}
|
||||
onChange={(value) => settingsMap.setKey('soundsFilter', value)}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ export function loadModules() {
|
|||
import('@strudel/serial'),
|
||||
import('@strudel/soundfonts'),
|
||||
import('@strudel/csound'),
|
||||
import('@strudel/tidal'),
|
||||
];
|
||||
if (isTauri()) {
|
||||
modules = modules.concat([
|
||||
|
|
|
|||
|
|
@ -21,16 +21,25 @@ export const patternFilterName = {
|
|||
user: 'user',
|
||||
};
|
||||
|
||||
export let $viewingPatternData = persistentAtom(
|
||||
'viewingPatternData',
|
||||
{
|
||||
id: '',
|
||||
code: '',
|
||||
collection: collectionName.user,
|
||||
created_at: Date.now(),
|
||||
},
|
||||
{ listen: false },
|
||||
);
|
||||
const sessionAtom = (name, initial = undefined) => {
|
||||
const storage = typeof sessionStorage !== 'undefined' ? sessionStorage : {};
|
||||
const store = atom(typeof storage[name] !== 'undefined' ? storage[name] : initial);
|
||||
store.listen((newValue) => {
|
||||
if (typeof newValue === 'undefined') {
|
||||
delete storage[name];
|
||||
} else {
|
||||
storage[name] = newValue;
|
||||
}
|
||||
});
|
||||
return store;
|
||||
};
|
||||
|
||||
export let $viewingPatternData = sessionAtom('viewingPatternData', {
|
||||
id: '',
|
||||
code: '',
|
||||
collection: collectionName.user,
|
||||
created_at: Date.now(),
|
||||
});
|
||||
|
||||
export const getViewingPatternData = () => {
|
||||
return parseJSON($viewingPatternData.get());
|
||||
|
|
@ -68,7 +77,7 @@ export async function loadDBPatterns() {
|
|||
}
|
||||
|
||||
// reason: https://github.com/tidalcycles/strudel/issues/857
|
||||
const $activePattern = persistentAtom('activePattern', '', { listen: false });
|
||||
const $activePattern = sessionAtom('activePattern', '');
|
||||
|
||||
export function setActivePattern(key) {
|
||||
$activePattern.set(key);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue