mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-10 05:08:30 +00:00
* 0.5 default cps * 1 -> 0.5 cps defaults * start moving examples to 2Hz * more 2Hz doc edits * small tweaks * format * adapt cycles page * adapt pitch page * tonal page * accumulation * synth page * adapt conditional-modifiers * audio effects page * adapt signals doc * fix: errors for signals * adapt signals page * start time modifiers * adapt time modifiers * adapt factories * hydra + pattern intro * adapt mini notation page * start recipes * adapt recipes page * use code_v1 table * delete old dbdump + add new csv based tool * fix: tests * fix: cpm * shuffle featured patterns * fix: snapshot --------- Co-authored-by: Felix Roos <flix91@gmail.com>
169 lines
5.2 KiB
JavaScript
169 lines
5.2 KiB
JavaScript
import { controls, evalScope, hash2code, logger } from '@strudel/core';
|
|
import { settingPatterns, defaultAudioDeviceName } from '../settings.mjs';
|
|
import { getAudioContext, initializeAudioOutput, setDefaultAudioContext } from '@strudel/webaudio';
|
|
|
|
import { isTauri } from '../tauri.mjs';
|
|
import './Repl.css';
|
|
|
|
import { createClient } from '@supabase/supabase-js';
|
|
import { nanoid } from 'nanoid';
|
|
import { writeText } from '@tauri-apps/api/clipboard';
|
|
import { createContext } from 'react';
|
|
import { $featuredPatterns, loadDBPatterns } from '@src/user_pattern_utils.mjs';
|
|
|
|
// Create a single supabase client for interacting with your database
|
|
export const supabase = createClient(
|
|
'https://pidxdsxphlhzjnzmifth.supabase.co',
|
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
|
|
);
|
|
|
|
let dbLoaded;
|
|
if (typeof window !== 'undefined') {
|
|
dbLoaded = loadDBPatterns();
|
|
}
|
|
|
|
export async function initCode() {
|
|
// load code from url hash (either short hash from database or decode long hash)
|
|
try {
|
|
const initialUrl = window.location.href;
|
|
const hash = initialUrl.split('?')[1]?.split('#')?.[0];
|
|
const codeParam = window.location.href.split('#')[1] || '';
|
|
// looking like https://strudel.cc/?J01s5i1J0200 (fixed hash length)
|
|
if (codeParam) {
|
|
// looking like https://strudel.cc/#ImMzIGUzIg%3D%3D (hash length depends on code length)
|
|
return hash2code(codeParam);
|
|
} else if (hash) {
|
|
return supabase
|
|
.from('code_v1')
|
|
.select('code')
|
|
.eq('hash', hash)
|
|
.then(({ data, error }) => {
|
|
if (error) {
|
|
console.warn('failed to load hash', error);
|
|
}
|
|
if (data.length) {
|
|
//console.log('load hash from database', hash);
|
|
return data[0].code;
|
|
}
|
|
});
|
|
}
|
|
} catch (err) {
|
|
console.warn('failed to decode', err);
|
|
}
|
|
}
|
|
|
|
export const parseJSON = (json) => {
|
|
json = json != null && json.length ? json : '{}';
|
|
try {
|
|
return JSON.parse(json);
|
|
} catch {
|
|
return '{}';
|
|
}
|
|
};
|
|
|
|
export async function getRandomTune() {
|
|
await dbLoaded;
|
|
const featuredTunes = Object.entries($featuredPatterns.get());
|
|
const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
const [_, data] = randomItem(featuredTunes);
|
|
return data;
|
|
}
|
|
|
|
export function loadModules() {
|
|
let modules = [
|
|
import('@strudel/core'),
|
|
import('@strudel/tonal'),
|
|
import('@strudel/mini'),
|
|
import('@strudel/xen'),
|
|
import('@strudel/webaudio'),
|
|
import('@strudel/codemirror'),
|
|
import('@strudel/hydra'),
|
|
import('@strudel/serial'),
|
|
import('@strudel/soundfonts'),
|
|
import('@strudel/csound'),
|
|
];
|
|
if (isTauri()) {
|
|
modules = modules.concat([
|
|
import('@strudel/desktopbridge/loggerbridge.mjs'),
|
|
import('@strudel/desktopbridge/midibridge.mjs'),
|
|
import('@strudel/desktopbridge/oscbridge.mjs'),
|
|
]);
|
|
} else {
|
|
modules = modules.concat([import('@strudel/midi'), import('@strudel/osc')]);
|
|
}
|
|
|
|
return evalScope(
|
|
controls, // sadly, this cannot be exported from core direclty
|
|
settingPatterns,
|
|
...modules,
|
|
);
|
|
}
|
|
|
|
let lastShared;
|
|
export async function shareCode(codeToShare) {
|
|
// const codeToShare = activeCode || code;
|
|
if (lastShared === codeToShare) {
|
|
logger(`Link already generated!`, 'error');
|
|
return;
|
|
}
|
|
const isPublic = confirm(
|
|
'Do you want your pattern to be public? If no, press cancel and you will get just a private link.',
|
|
);
|
|
// generate uuid in the browser
|
|
const hash = nanoid(12);
|
|
const shareUrl = window.location.origin + window.location.pathname + '?' + hash;
|
|
const { error } = await supabase.from('code_v1').insert([{ code: codeToShare, hash, ['public']: isPublic }]);
|
|
if (!error) {
|
|
lastShared = codeToShare;
|
|
// copy shareUrl to clipboard
|
|
if (isTauri()) {
|
|
await writeText(shareUrl);
|
|
} else {
|
|
await navigator.clipboard.writeText(shareUrl);
|
|
}
|
|
const message = `Link copied to clipboard: ${shareUrl}`;
|
|
alert(message);
|
|
// alert(message);
|
|
logger(message, 'highlight');
|
|
} else {
|
|
console.log('error', error);
|
|
const message = `Error: ${error.message}`;
|
|
// alert(message);
|
|
logger(message);
|
|
}
|
|
}
|
|
|
|
export const ReplContext = createContext(null);
|
|
|
|
export const getAudioDevices = async () => {
|
|
await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
let mediaDevices = await navigator.mediaDevices.enumerateDevices();
|
|
mediaDevices = mediaDevices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default');
|
|
const devicesMap = new Map();
|
|
devicesMap.set(defaultAudioDeviceName, '');
|
|
mediaDevices.forEach((device) => {
|
|
devicesMap.set(device.label, device.deviceId);
|
|
});
|
|
return devicesMap;
|
|
};
|
|
|
|
export const setAudioDevice = async (id) => {
|
|
let audioCtx = getAudioContext();
|
|
if (audioCtx.sinkId === id) {
|
|
return;
|
|
}
|
|
await audioCtx.suspend();
|
|
await audioCtx.close();
|
|
audioCtx = setDefaultAudioContext();
|
|
await audioCtx.resume();
|
|
const isValidID = (id ?? '').length > 0;
|
|
if (isValidID) {
|
|
try {
|
|
await audioCtx.setSinkId(id);
|
|
} catch {
|
|
logger('failed to set audio interface', 'warning');
|
|
}
|
|
}
|
|
initializeAudioOutput();
|
|
};
|