Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Workshop #587

Merged
merged 37 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2177e13
Merge branch 'vanilla-highlighting' into workshop
felixroos May 25, 2023
82225f0
started workshop pages
felixroos May 26, 2023
0d6fcf7
hide mini repl headers + improve workshop
felixroos May 26, 2023
fc06181
- add claviature flag to minirepl
felixroos May 27, 2023
4e575c4
begin first notes page
felixroos May 27, 2023
ed792fc
continue notes chapter
felixroos May 27, 2023
1ba5d2e
finish notes chapter
felixroos May 27, 2023
8c93e57
clamp delayfeedback
felixroos May 28, 2023
9971867
clamp function
felixroos May 28, 2023
d2dffe3
MiniRepl: consume font settings
felixroos May 28, 2023
1e9979a
start fleshing out effects chapter
felixroos May 28, 2023
8679dc6
pianoroll: also reflect gain in transparency
felixroos May 29, 2023
536327f
effects chapter mostly finished
felixroos May 29, 2023
39d5955
add function recap to sounds chapter
felixroos May 29, 2023
8b7bb7b
add adsr section to effects chapter
felixroos May 29, 2023
0edd7e1
add compound adsr + ds controls
felixroos May 29, 2023
c4a38d9
+ pattern effects chapter
felixroos May 30, 2023
f0faf5b
Merge remote-tracking branch 'origin/main' into workshop-new
felixroos Jun 5, 2023
61a9b01
started german translation of workshop
felixroos Jun 5, 2023
a88759b
translate notes chapter
felixroos Jun 6, 2023
06950a6
translate rest of workshop to german
felixroos Jun 7, 2023
02d793e
Merge remote-tracking branch 'origin/main' into workshop-new
felixroos Jun 7, 2023
105193a
replace tutorial with workshop
felixroos Jun 7, 2023
119a66a
- translate getting-started
felixroos Jun 7, 2023
37c4541
translate link
felixroos Jun 7, 2023
eccce6f
remove dirt samples
felixroos Jun 8, 2023
f8c3e3d
add missing sounds by hand
felixroos Jun 8, 2023
ffffdd2
fix: format
felixroos Jun 8, 2023
1fec2f7
fix: build
felixroos Jun 8, 2023
51cc692
fix: repl link to docs was old
felixroos Jun 8, 2023
3ccbd35
add label support to pianoroll
felixroos Jun 8, 2023
c287ff5
optimize sounds chapter after teach test
felixroos Jun 8, 2023
ae868c6
hide gutter in mini repl
felixroos Jun 8, 2023
89a4a85
more friendly heading
felixroos Jun 9, 2023
7dc2a00
more detail
felixroos Jun 9, 2023
576b882
Merge remote-tracking branch 'origin/main' into workshop-new
felixroos Jun 9, 2023
e10104d
bring de improvements to en workshop
felixroos Jun 9, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 12 additions & 1 deletion packages/core/controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

import { Pattern, sequence } from './pattern.mjs';
import { Pattern, register, sequence } from './pattern.mjs';
import { zipWith } from './util.mjs';

const controls = {};
Expand Down Expand Up @@ -810,4 +810,15 @@ generic_params.forEach(([names, ...aliases]) => {
controls.createParams = (...names) =>
names.reduce((acc, name) => Object.assign(acc, { [name]: controls.createParam(name) }), {});

controls.adsr = register('adsr', (adsr, pat) => {
adsr = !Array.isArray(adsr) ? [adsr] : adsr;
const [attack, decay, sustain, release] = adsr;
return pat.set({ attack, decay, sustain, release });
});
controls.ds = register('ds', (ds, pat) => {
ds = !Array.isArray(ds) ? [ds] : ds;
const [decay, sustain] = ds;
return pat.set({ decay, sustain });
});

export default controls;
22 changes: 20 additions & 2 deletions packages/core/pianoroll.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Pattern.prototype.pianoroll = function ({
timeframe: timeframeProp,
fold = 0,
vertical = 0,
labels = 0,
} = {}) {
const ctx = getDrawContext();
const w = ctx.canvas.width;
Expand Down Expand Up @@ -87,7 +88,7 @@ Pattern.prototype.pianoroll = function ({
const isActive = event.whole.begin <= t && event.whole.end > t;
ctx.fillStyle = event.context?.color || inactive;
ctx.strokeStyle = event.context?.color || active;
ctx.globalAlpha = event.context.velocity ?? 1;
ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1;
const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange);
let durationPx = scale(event.duration / timeExtent, 0, timeAxis);
const value = getValue(event);
Expand All @@ -114,6 +115,14 @@ Pattern.prototype.pianoroll = function ({
];
}
isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords);
if (labels) {
const label = event.value.note ?? event.value.s + (event.value.n ? `:${event.value.n}` : '');
ctx.font = `${barThickness * 0.75}px monospace`;
ctx.strokeStyle = 'black';
ctx.fillStyle = isActive ? 'white' : 'black';
ctx.textBaseline = 'top';
ctx.fillText(label, ...coords);
}
});
ctx.globalAlpha = 1; // reset!
const playheadPosition = scale(-from / timeExtent, ...timeRange);
Expand Down Expand Up @@ -181,6 +190,7 @@ export function pianoroll({
timeframe: timeframeProp,
fold = 0,
vertical = 0,
labels = false,
ctx,
} = {}) {
const w = ctx.canvas.width;
Expand Down Expand Up @@ -240,7 +250,7 @@ export function pianoroll({
const color = event.value?.color || event.context?.color;
ctx.fillStyle = color || inactive;
ctx.strokeStyle = color || active;
ctx.globalAlpha = event.context.velocity ?? 1;
ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1;
const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange);
let durationPx = scale(event.duration / timeExtent, 0, timeAxis);
const value = getValue(event);
Expand All @@ -267,6 +277,14 @@ export function pianoroll({
];
}
isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords);
if (labels) {
const label = event.value.note ?? event.value.s + (event.value.n ? `:${event.value.n}` : '');
ctx.font = `${barThickness * 0.75}px monospace`;
ctx.strokeStyle = 'black';
ctx.fillStyle = isActive ? 'white' : 'black';
ctx.textBaseline = 'top';
ctx.fillText(label, ...coords);
}
});
ctx.globalAlpha = 1; // reset!
const playheadPosition = scale(-from / timeExtent, ...timeRange);
Expand Down
5 changes: 4 additions & 1 deletion packages/core/util.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,14 @@ export const getFreq = (noteOrMidi) => {
return midiToFreq(noteToMidi(noteOrMidi));
};

const pcs = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'];
/**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete
*/
export const midi2note = (n) => {
const oct = Math.floor(n / 12) - 1;
const pc = ['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'Bb', 'B'][n % 12];
const pc = pcs[n % 12];
return pc + oct;
};

Expand Down Expand Up @@ -212,3 +213,5 @@ export const splitAt = function (index, value) {
};

export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i]));

export const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
90 changes: 61 additions & 29 deletions packages/react/src/components/MiniRepl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,24 @@ export function MiniRepl({
tune,
hideOutsideView = false,
enableKeyboard,
onTrigger,
drawTime,
punchcard,
punchcardLabels,
onPaint,
canvasHeight = 200,
fontSize = 18,
fontFamily,
hideHeader = false,
theme,
keybindings,
isLineNumbersDisplayed,
}) {
drawTime = drawTime || (punchcard ? [0, 4] : undefined);
const evalOnMount = !!drawTime;
const drawContext = useCallback(
!!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
[drawTime],
punchcard ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
[punchcard],
);
const {
code,
Expand All @@ -47,7 +55,18 @@ export function MiniRepl({
} = useStrudel({
initialCode: tune,
defaultOutput: webaudioOutput,
editPattern: (pat) => (punchcard ? pat.punchcard() : pat),
editPattern: (pat, id) => {
//pat = pat.withContext((ctx) => ({ ...ctx, id }));
if (onTrigger) {
pat = pat.onTrigger(onTrigger, false);
}
if (onPaint) {
pat = pat.onPaint(onPaint);
} else if (punchcard) {
pat = pat.punchcard({ labels: punchcardLabels });
}
return pat;
},
getTime,
evalOnMount,
drawContext,
Expand Down Expand Up @@ -82,7 +101,7 @@ export function MiniRepl({
e.preventDefault();
flash(view);
await activateCode();
} else if (e.key === '.') {
} else if (e.key === '.' || e.code === 'Period') {
stop();
e.preventDefault();
}
Expand All @@ -101,41 +120,54 @@ export function MiniRepl({
// const logId = data?.pattern?.meta?.id;
if (logId === replId) {
setLog((l) => {
return l.concat([e.detail]).slice(-10);
return l.concat([e.detail]).slice(-8);
});
}
}, []),
);

return (
<div className="overflow-hidden rounded-t-md bg-background border border-lineHighlight" ref={ref}>
<div className="flex justify-between bg-lineHighlight">
<div className="flex">
<button
className={cx(
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background',
started ? 'animate-pulse' : '',
)}
onClick={() => togglePlay()}
>
<Icon type={started ? 'stop' : 'play'} />
</button>
<button
className={cx(
'w-16 flex items-center justify-center p-1 text-foreground border-lineHighlight bg-lineHighlight',
isDirty ? 'text-foreground hover:bg-background cursor-pointer' : 'opacity-50 cursor-not-allowed',
)}
onClick={() => activateCode()}
>
<Icon type="refresh" />
</button>
{!hideHeader && (
<div className="flex justify-between bg-lineHighlight">
<div className="flex">
<button
className={cx(
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background',
started ? 'animate-pulse' : '',
)}
onClick={() => togglePlay()}
>
<Icon type={started ? 'stop' : 'play'} />
</button>
<button
className={cx(
'w-16 flex items-center justify-center p-1 text-foreground border-lineHighlight bg-lineHighlight',
isDirty ? 'text-foreground hover:bg-background cursor-pointer' : 'opacity-50 cursor-not-allowed',
)}
onClick={() => activateCode()}
>
<Icon type="refresh" />
</button>
</div>
</div>
{error && <div className="text-right p-1 text-sm text-red-200">{error.message}</div>}
</div>
)}
<div className="overflow-auto relative">
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} theme={theme} />}
{show && (
<CodeMirror6
value={code}
onChange={setCode}
onViewChanged={setView}
theme={theme}
fontFamily={fontFamily}
fontSize={fontSize}
keybindings={keybindings}
isLineNumbersDisplayed={isLineNumbersDisplayed}
/>
)}
{error && <div className="text-right p-1 text-md text-red-200">{error.message}</div>}
</div>
{drawTime && (
{punchcard && (
<canvas
id={canvasId}
className="w-full pointer-events-none"
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/hooks/useStrudel.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ function useStrudel({
const [pattern, setPattern] = useState();
const [started, setStarted] = useState(false);
const isDirty = code !== activeCode;
const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
//const shouldPaint = useCallback((pat) => !!(pat?.context?.onPaint && drawContext), [drawContext]);
const shouldPaint = useCallback((pat) => !!pat?.context?.onPaint, []);

// TODO: make sure this hook reruns when scheduler.started changes
const { scheduler, evaluate, start, stop, pause, setCps } = useMemo(
Expand Down
5 changes: 5 additions & 0 deletions packages/webaudio/webaudio.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,12 @@ export async function initAudioOnFirstClick() {
}

let delays = {};
const maxfeedback = 0.98;
function getDelay(orbit, delaytime, delayfeedback, t) {
if (delayfeedback > maxfeedback) {
logger(`delayfeedback was clamped to ${maxfeedback} to save your ears`);
}
delayfeedback = strudel.clamp(delayfeedback, 0, 0.98);
if (!delays[orbit]) {
const ac = getAudioContext();
const dly = ac.createFeedbackDelay(1, delaytime, delayfeedback);
Expand Down