Compare commits

..

1 Commits

Author SHA1 Message Date
Felix Roos e3c34f22fd wip websocket client 2025-09-07 22:41:25 +02:00
36 changed files with 92 additions and 192 deletions
-63
View File
@@ -1,63 +0,0 @@
import {
keymap,
highlightSpecialChars,
drawSelection,
highlightActiveLine,
dropCursor,
rectangularSelection,
crosshairCursor,
lineNumbers,
highlightActiveLineGutter,
} from '@codemirror/view';
import {
defaultHighlightStyle,
syntaxHighlighting,
bracketMatching,
foldGutter,
foldKeymap,
} from '@codemirror/language';
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
import { searchKeymap, highlightSelectionMatches } from '@codemirror/search';
import { completionKeymap, closeBracketsKeymap } from '@codemirror/autocomplete';
// Taken + slightly modified from https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
export const basicSetup = (() => [
// lineNumbers(),
// highlightActiveLineGutter(),
highlightSpecialChars(),
history(),
// foldGutter(),
// drawSelection(),
dropCursor(),
// EditorState.allowMultipleSelections.of(true),
// indentOnInput(),
// syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
// autocompletion(),
rectangularSelection(),
crosshairCursor(),
// highlightActiveLine(),
// highlightSelectionMatches(),
keymap.of([
...closeBracketsKeymap,
...defaultKeymap,
// ...searchKeymap,
...historyKeymap,
// ...foldKeymap,
// ...completionKeymap,
]),
])();
/// A minimal set of extensions to create a functional editor. Only
/// includes [the default keymap](#commands.defaultKeymap), [undo
/// history](#commands.history), [special character
/// highlighting](#view.highlightSpecialChars), [custom selection
/// drawing](#view.drawSelection), and [default highlight
/// style](#language.defaultHighlightStyle).
export const minimalSetup = (() => [
highlightSpecialChars(),
history(),
drawSelection(),
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
keymap.of([...defaultKeymap, ...historyKeymap]),
])();
+3 -8
View File
@@ -1,8 +1,8 @@
import { closeBrackets } from '@codemirror/autocomplete';
export { toggleComment, toggleBlockComment, toggleLineComment, toggleBlockCommentByLine } from '@codemirror/commands';
// import { search, highlightSelectionMatches } from '@codemirror/search';
import { indentWithTab } from '@codemirror/commands';
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript';
import { history, indentWithTab } from '@codemirror/commands';
import { javascript } from '@codemirror/lang-javascript';
import { defaultHighlightStyle, syntaxHighlighting, bracketMatching } from '@codemirror/language';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import {
@@ -24,7 +24,6 @@ import { initTheme, activateTheme, theme } from './themes.mjs';
import { sliderPlugin, updateSliderWidgets } from './slider.mjs';
import { widgetPlugin, updateWidgets } from './widget.mjs';
import { persistentAtom } from '@nanostores/persistent';
import { basicSetup } from './basicSetup.mjs';
const extensions = {
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
@@ -86,17 +85,13 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo
/* search(),
highlightSelectionMatches(), */
...initialSettings,
basicSetup,
mondo ? [] : javascript(),
javascriptLanguage.data.of({
closeBrackets: { brackets: ['(', '[', '{', "'", '"', '<'] },
bracketMatching: { brackets: ['(', '[', '{', "'", '"', '<'] },
}),
sliderPlugin,
widgetPlugin,
// indentOnInput(), // works without. already brought with javascript extension?
// bracketMatching(), // does not do anything
syntaxHighlighting(defaultHighlightStyle),
history(),
EditorView.updateListener.of((v) => onChange(v)),
drawSelection({ cursorBlinkRate: 0 }),
Prec.highest(
+2 -3
View File
@@ -3,9 +3,8 @@ import { keymap, ViewPlugin } from '@codemirror/view';
// import { searchKeymap } from '@codemirror/search';
import { emacs } from '@replit/codemirror-emacs';
import { vim } from '@replit/codemirror-vim';
// import { vim } from './vim_test.mjs';
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
import { defaultKeymap } from '@codemirror/commands';
import { defaultKeymap, historyKeymap } from '@codemirror/commands';
const vscodePlugin = ViewPlugin.fromClass(
class {
@@ -28,5 +27,5 @@ const keymaps = {
export function keybindings(name) {
const active = keymaps[name];
return [active ? Prec.high(active()) : []];
return [active ? active() : [], keymap.of(historyKeymap)];
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/codemirror",
"version": "1.2.5",
"version": "1.2.3",
"description": "Codemirror Extensions for Strudel",
"main": "index.mjs",
"publishConfig": {
@@ -42,7 +42,7 @@
"@lezer/highlight": "^1.2.1",
"@nanostores/persistent": "^0.10.2",
"@replit/codemirror-emacs": "^6.1.0",
"@replit/codemirror-vim": "^6.3.0",
"@replit/codemirror-vim": "^6.2.1",
"@replit/codemirror-vscode-keymap": "^6.0.2",
"@strudel/core": "workspace:*",
"@strudel/draw": "workspace:*",
+1 -1
View File
@@ -6,7 +6,7 @@ let debounce = 1000,
export function errorLogger(e, origin = 'cyclist') {
//TODO: add some kind of debug flag that enables this while in dev mode
// console.error(e);
console.error(e);
logger(`[${origin}] error: ${e.message}`);
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/core",
"version": "1.2.4",
"version": "1.2.3",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/csound",
"version": "1.2.5",
"version": "1.2.4",
"description": "csound bindings for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/draw",
"version": "1.2.4",
"version": "1.2.3",
"description": "Helpers for drawing with Strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/gamepad",
"version": "1.2.4",
"version": "1.2.3",
"description": "Gamepad Inputs for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/hydra",
"version": "1.2.4",
"version": "1.2.3",
"description": "Hydra integration for strudel",
"main": "hydra.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/midi",
"version": "1.2.5",
"version": "1.2.4",
"description": "Midi API for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/mini",
"version": "1.2.4",
"version": "1.2.3",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/mondo",
"version": "1.1.2",
"version": "1.1.1",
"description": "mondo notation for strudel",
"main": "mondough.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/motion",
"version": "1.2.4",
"version": "1.2.3",
"description": "DeviceMotion API for strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/mqtt",
"version": "1.2.4",
"version": "1.2.3",
"description": "MQTT API for strudel",
"main": "mqtt.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/osc",
"version": "1.2.4",
"version": "1.2.3",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"type": "module",
+1 -1
View File
@@ -6,5 +6,5 @@ const trigger = /* isTauri() ? oscTriggerTauri : */ oscTrigger;
export const superdirtOutput = (hap, deadline, hapDuration, cps, targetTime) => {
const currentTime = performance.now() / 1000;
return trigger(hap, currentTime, cps, targetTime);
return trigger(null, hap, currentTime, cps, targetTime);
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/repl",
"version": "1.2.6",
"version": "1.2.4",
"description": "Strudel REPL as a Web Component",
"module": "index.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/sampler",
"version": "0.2.3",
"version": "0.2.2",
"description": "",
"keywords": [
"tidalcycles",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/serial",
"version": "1.2.4",
"version": "1.2.3",
"description": "Webserial API for strudel",
"main": "serial.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/soundfonts",
"version": "1.2.5",
"version": "1.2.4",
"description": "Soundsfont support for strudel",
"main": "index.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "superdough",
"version": "1.2.5",
"version": "1.2.4",
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/tonal",
"version": "1.2.4",
"version": "1.2.3",
"description": "Tonal functions for strudel",
"main": "index.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/transpiler",
"version": "1.2.4",
"version": "1.2.3",
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/web",
"version": "1.2.5",
"version": "1.2.4",
"description": "Easy to setup, opiniated bundle of Strudel for the browser.",
"module": "web.mjs",
"publishConfig": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/webaudio",
"version": "1.2.5",
"version": "1.2.4",
"description": "Web Audio helpers for Strudel",
"main": "index.mjs",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel/xen",
"version": "1.2.4",
"version": "1.2.3",
"description": "Xenharmonic API for strudel",
"main": "index.mjs",
"type": "module",
+10 -13
View File
@@ -201,8 +201,8 @@ importers:
specifier: ^6.1.0
version: 6.1.0(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
'@replit/codemirror-vim':
specifier: ^6.3.0
version: 6.3.0(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
specifier: ^6.2.1
version: 6.2.1(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
'@replit/codemirror-vscode-keymap':
specifier: ^6.0.2
version: 6.0.2(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)
@@ -2232,14 +2232,14 @@ packages:
'@codemirror/state': ^6.0.1
'@codemirror/view': ^6.3.0
'@replit/codemirror-vim@6.3.0':
resolution: {integrity: sha512-aTx931ULAMuJx6xLf7KQDOL7CxD+Sa05FktTDrtLaSy53uj01ll3Zf17JdKsriER248oS55GBzg0CfCTjEneAQ==}
'@replit/codemirror-vim@6.2.1':
resolution: {integrity: sha512-qDAcGSHBYU5RrdO//qCmD8K9t6vbP327iCj/iqrkVnjbrpFhrjOt92weGXGHmTNRh16cUtkUZ7Xq7rZf+8HVow==}
peerDependencies:
'@codemirror/commands': 6.x.x
'@codemirror/language': 6.x.x
'@codemirror/search': 6.x.x
'@codemirror/state': 6.x.x
'@codemirror/view': 6.x.x
'@codemirror/commands': ^6.0.0
'@codemirror/language': ^6.1.0
'@codemirror/search': ^6.2.0
'@codemirror/state': ^6.0.1
'@codemirror/view': ^6.0.3
'@replit/codemirror-vscode-keymap@6.0.2':
resolution: {integrity: sha512-j45qTwGxzpsv82lMD/NreGDORFKSctMDVkGRopaP+OrzSzv+pXDQuU3LnFvKpasyjVT0lf+PKG1v2DSCn/vxxg==}
@@ -5714,7 +5714,6 @@ packages:
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
node-fetch-native@1.6.6:
resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==}
@@ -6804,7 +6803,6 @@ packages:
source-map@0.8.0-beta.0:
resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
engines: {node: '>= 8'}
deprecated: The work that was done in this beta branch won't be included in future versions
sourcemap-codec@1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
@@ -7659,7 +7657,6 @@ packages:
workbox-google-analytics@7.0.0:
resolution: {integrity: sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==}
deprecated: It is not compatible with newer versions of GA starting with v4, as long as you are using GAv3 it should be ok, but the package is not longer being maintained
workbox-navigation-preload@7.0.0:
resolution: {integrity: sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==}
@@ -9598,7 +9595,7 @@ snapshots:
'@codemirror/state': 6.5.1
'@codemirror/view': 6.36.2
'@replit/codemirror-vim@6.3.0(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
'@replit/codemirror-vim@6.2.1(@codemirror/commands@6.8.0)(@codemirror/language@6.10.8)(@codemirror/search@6.5.8)(@codemirror/state@6.5.1)(@codemirror/view@6.36.2)':
dependencies:
'@codemirror/commands': 6.8.0
'@codemirror/language': 6.10.8
-16
View File
@@ -168,20 +168,6 @@ Using "!" we can repeat without speeding up:
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>*2")`} punchcard />
## Randomness
Events with a "?" placed after them will have a 50% chance of being removed from the pattern:
<MiniRepl client:idle tune={`note("[g3,b3,e4]*8?")`} punchcard />
Adding a number between 0 and 1 after the "?" will affect the likelihood of the event being removed. For example, events with "?0.1" placed after them will have a 10% chance of being removed:
<MiniRepl client:idle tune={`note("[g3,b3,e4]*8?0.1")`} punchcard />
Events separated by a "|" will be chosen from at random:
<MiniRepl client:idle tune={`note("[g3,b3,e4] | [a3,c3,e4] | [b3,d3,f#4]")`} punchcard />
## Mini-notation review
To recap what we've learned so far, compare the following patterns:
@@ -193,8 +179,6 @@ To recap what we've learned so far, compare the following patterns:
<MiniRepl client:idle tune={`note("<[g3,b3,e4] _ [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]? [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3|b3|e4] [a3,c3,e4] [b3,d3,f#4]>*2")`} />
## Euclidian rhythms
+1 -3
View File
@@ -9,9 +9,7 @@ import { MiniRepl } from '../../docs/MiniRepl';
{/* The [REPL](https://strudel.cc/) is the place where all packages come together to form a live coding system. It can also be seen as a reference implementation for users of the library. */}
While Strudel can be used as a library in any JavaScript codebase, its main, reference user interface is the Strudel REPL[^1], which is a browser-based live coding environment. This live code editor is dedicated to manipulating Strudel patterns while they play. The REPL features built-in visual feedback, highlighting which elements in the patterned (mini-notation) sequences are influencing the event that is currently being played. This feedback is designed to support both learning and live use of Strudel.
[^1]: REPL stands for read, evaluate, print/play, loop. It is friendly jargon for an interactive programming interface from computing heritage, usually for a commandline interface but also applied to live coding editors.
While Strudel can be used as a library in any JavaScript codebase, its main, reference user interface is the Strudel REPL^[REPL stands for read, evaluate, print/play, loop. It is friendly jargon for an interactive programming interface from computing heritage, usually for a commandline interface but also applied to live coding editors.], which is a browser-based live coding environment. This live code editor is dedicated to manipulating Strudel patterns while they play. The REPL features built-in visual feedback, highlighting which elements in the patterned (mini-notation) sequences are influencing the event that is currently being played. This feedback is designed to support both learning and live use of Strudel.
Besides a UI for playback control and meta information, the main part of the REPL interface is the code editor powered by CodeMirror. In it, the user can edit and evaluate pattern code live, using one of the available synthesis outputs to create music and/or sound art. The control flow of the REPL follows 3 basic steps:
@@ -1,10 +0,0 @@
import cx from '@src/cx.mjs';
export function ActionButton({ children, label, labelIsHidden, className, ...buttonProps }) {
return (
<button className={cx('hover:opacity-50 text-nowrap w-fit', className)} title={label} {...buttonProps}>
{labelIsHidden !== true && label}
{children}
</button>
);
}
@@ -12,8 +12,8 @@ import { useMemo } from 'react';
import { getMetadata } from '../../../metadata_parser.js';
import { useExamplePatterns } from '../../useExamplePatterns.jsx';
import { parseJSON, isUdels } from '../../util.mjs';
import { useSettings } from '../../../settings.mjs';
import { ActionButton } from '../button/action-button.jsx';
import { ButtonGroup } from './Forms.jsx';
import { settingsMap, useSettings } from '../../../settings.mjs';
import { Pagination } from '../pagination/Pagination.jsx';
import { useState } from 'react';
import { useDebounce } from '../usedebounce.jsx';
@@ -75,6 +75,15 @@ function PatternButtons({ patterns, activePattern, onClick, started }) {
);
}
function ActionButton({ children, onClick, label, labelIsHidden }) {
return (
<button className="hover:opacity-50 text-nowrap" onClick={onClick} title={label}>
{labelIsHidden !== true && label}
{children}
</button>
);
}
const updateCodeWindow = (context, patternData, reset = false) => {
context.handleUpdate(patternData, reset);
};
+11 -34
View File
@@ -2,21 +2,16 @@ import useEvent from '@src/useEvent.mjs';
import { useStore } from '@nanostores/react';
import { getAudioContext, soundMap, connectToDestination } from '@strudel/webaudio';
import { useMemo, useRef, useState } from 'react';
import { settingsMap, soundFilterType, useSettings } from '../../../settings.mjs';
import { settingsMap, useSettings } from '../../../settings.mjs';
import { ButtonGroup } from './Forms.jsx';
import ImportSoundsButton from './ImportSoundsButton.jsx';
import { Textbox } from '../textbox/Textbox.jsx';
import { ActionButton } from '../button/action-button.jsx';
import { confirmDialog } from '@src/repl/util.mjs';
import { clearIDB, userSamplesDBConfig } from '@src/repl/idbutils.mjs';
import { prebake } from '@src/repl/prebake.mjs';
const getSamples = (samples) =>
Array.isArray(samples) ? samples.length : typeof samples === 'object' ? Object.values(samples).length : 1;
export function SoundsTab() {
const sounds = useStore(soundMap);
const { soundsFilter } = useSettings();
const [search, setSearch] = useState('');
const { BASE_URL } = import.meta.env;
@@ -32,19 +27,18 @@ export function SoundsTab() {
.sort((a, b) => a[0].localeCompare(b[0]))
.filter(([name]) => name.toLowerCase().includes(search.toLowerCase()));
if (soundsFilter === soundFilterType.USER) {
if (soundsFilter === 'user') {
return filtered.filter(([_, { data }]) => !data.prebake);
}
if (soundsFilter === soundFilterType.DRUMS) {
if (soundsFilter === 'drums') {
return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag === 'drum-machines');
}
if (soundsFilter === soundFilterType.SAMPLES) {
if (soundsFilter === 'samples') {
return filtered.filter(([_, { data }]) => data.type === 'sample' && data.tag !== 'drum-machines');
}
if (soundsFilter === soundFilterType.SYNTHS) {
if (soundsFilter === 'synths') {
return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type));
}
//TODO: tidy this up, it does not need to be saved in settings
if (soundsFilter === 'importSounds') {
return [];
}
@@ -63,10 +57,10 @@ export function SoundsTab() {
});
});
return (
<div id="sounds-tab" className="px-4 flex gap-2 flex-col w-full h-full text-foreground">
<div id="sounds-tab" className="px-4 flex flex-col w-full h-full text-foreground">
<Textbox placeholder="Search" value={search} onChange={(v) => setSearch(v)} />
<div className=" flex shrink-0 flex-wrap">
<div className="pb-2 flex shrink-0 flex-wrap">
<ButtonGroup
value={soundsFilter}
onChange={(value) => settingsMap.setKey('soundsFilter', value)}
@@ -80,26 +74,7 @@ export function SoundsTab() {
></ButtonGroup>
</div>
{soundsFilter === soundFilterType.USER && soundEntries.length > 0 && (
<ActionButton
className="pl-2"
label="delete-all"
onClick={async () => {
try {
const confirmed = await confirmDialog('Delete all imported user samples?');
if (confirmed) {
clearIDB(userSamplesDBConfig.dbName);
soundMap.set({});
await prebake();
}
} catch (e) {
console.error(e);
}
}}
/>
)}
<div className="min-h-0 max-h-full grow overflow-auto text-sm break-normal bg-background p-2 rounded-md">
<div className="min-h-0 max-h-full grow overflow-auto text-sm break-normal pb-2">
{soundEntries.map(([name, { data, onTrigger }]) => {
return (
<span
@@ -176,7 +151,9 @@ export function SoundsTab() {
) : (
''
)}
{!soundEntries.length && soundsFilter !== 'importSounds' ? 'No sounds loaded' : ''}
{!soundEntries.length && soundsFilter !== 'importSounds'
? 'No custom sounds loaded in this pattern (yet).'
: ''}
</div>
</div>
);
+2 -6
View File
@@ -12,21 +12,17 @@ export const userSamplesDBConfig = {
};
// deletes all of the databases, useful for debugging
function clearAllIDB() {
function clearIDB() {
window.indexedDB
.databases()
.then((r) => {
for (var i = 0; i < r.length; i++) clearIDB(r[i].name);
for (var i = 0; i < r.length; i++) window.indexedDB.deleteDatabase(r[i].name);
})
.then(() => {
alert('All data cleared.');
});
}
export function clearIDB(dbName) {
return window.indexedDB.deleteDatabase(dbName);
}
// queries the DB, and registers the sounds so they can be played
export function registerSamplesFromDB(config = userSamplesDBConfig, onComplete = () => {}) {
openDB(config, (objectStore) => {
+26
View File
@@ -0,0 +1,26 @@
import { register, signal } from '@strudel/core';
import { clamp } from '../../../packages/superdough/util.mjs';
let sigs = [0, 0, 0, 0, 0, 0, 0, 0];
export let sig = register('sig', (pat) =>
pat
.fmap((n) => {
return signal(() => clamp(sigs[n], 0.0001, 1));
})
.outerJoin(),
);
let socket;
export function initSocket(ip) {
// e.g. 192.168.1.12:4422
socket = socket || new WebSocket(`ws://${ip}`);
socket.addEventListener('open', () => console.log('connection opened'));
socket.addEventListener('error', () => console.log('connection error'));
socket.addEventListener('message', (event) => {
if (Math.random() < 0.01) {
console.log(event.data.toString());
}
sigs = JSON.parse(event.data.toString());
});
}
+1 -9
View File
@@ -8,14 +8,6 @@ export const audioEngineTargets = {
osc: 'osc',
};
export const soundFilterType = {
USER: 'user',
DRUMS: 'drums',
SAMPLES: 'samples',
SYNTHS: 'synths',
ALL: 'all',
};
export const defaultSettings = {
activeFooter: 'intro',
keybindings: 'codemirror',
@@ -36,7 +28,7 @@ export const defaultSettings = {
fontSize: 18,
latestCode: '',
isZen: false,
soundsFilter: soundFilterType.ALL,
soundsFilter: 'all',
patternFilter: 'community',
// panelPosition: window.innerWidth > 1000 ? 'right' : 'bottom', //FIX: does not work on astro
panelPosition: 'right',