From dbd77211cb342cb5737b2cc008865ce5fadab1fc Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 29 Oct 2023 22:28:55 +0100 Subject: [PATCH 1/7] Adding basic autocompletion --- src/EditorSetup.ts | 9 +++++++-- src/documentation/inlineHelp.ts | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/EditorSetup.ts b/src/EditorSetup.ts index 944454d..3b22b16 100644 --- a/src/EditorSetup.ts +++ b/src/EditorSetup.ts @@ -34,6 +34,12 @@ import { EditorView } from "codemirror"; import { toposTheme } from "./themes/toposTheme"; import { javascript } from "@codemirror/lang-javascript"; import { inlineHoveringTips } from "./documentation/inlineHelp"; +import { toposCompletions } from "./documentation/inlineHelp"; +import { javascriptLanguage } from "@codemirror/lang-javascript" + +const jsCompletions = javascriptLanguage.data.of({ + autocomplete: toposCompletions +}) export const editorSetup: Extension = (() => [ highlightActiveLineGutter(), @@ -47,8 +53,7 @@ export const editorSetup: Extension = (() => [ bracketMatching(), closeBrackets(), autocompletion(), - // rectangularSelection(), - // crosshairCursor(), + jsCompletions, highlightActiveLine(), highlightSelectionMatches(), keymap.of([ diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts index ef6bc24..fe1a21b 100644 --- a/src/documentation/inlineHelp.ts +++ b/src/documentation/inlineHelp.ts @@ -1,5 +1,7 @@ import { hoverTooltip } from "@codemirror/view"; import { type EditorView } from "@codemirror/view"; +import { CompletionContext } from "@codemirror/autocomplete" + interface InlineCompletion { name: string; @@ -968,3 +970,21 @@ export const inlineHoveringTips = hoverTooltip( }; } ); + + +export const toposCompletions = (context: CompletionContext) => { + let word = context.matchBefore(/\w*/) + if (word) { + if (word.from == word.to && !context.explicit) + return null + return { + from: word.from, + options: Object.keys(completionDatabase).map((key) => ({ + label: key, + type: completionDatabase[key].category, + info: `${completionDatabase[key].description}`, + })) + } + } +} + From 72747c961a815e112000270ef273749a7f29ecd8 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 29 Oct 2023 22:48:03 +0100 Subject: [PATCH 2/7] Better completion style --- src/documentation/inlineHelp.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts index fe1a21b..0b182e6 100644 --- a/src/documentation/inlineHelp.ts +++ b/src/documentation/inlineHelp.ts @@ -982,7 +982,16 @@ export const toposCompletions = (context: CompletionContext) => { options: Object.keys(completionDatabase).map((key) => ({ label: key, type: completionDatabase[key].category, - info: `${completionDatabase[key].description}`, + info: () => { + let div = document.createElement('div'); + div.innerHTML = ` +

${completionDatabase[key].name} [${completionDatabase[key].category}]

+

${completionDatabase[key].description}

+
${completionDatabase[key].example}
+ ` + div.classList.add("px-4", "py-2", "rounded-lg", "w-92"); + return div + } })) } } From a105028f100405141c8271019347276ad56b9ec3 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 29 Oct 2023 22:53:24 +0100 Subject: [PATCH 3/7] overflow for longer examples --- src/documentation/inlineHelp.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts index 0b182e6..658c806 100644 --- a/src/documentation/inlineHelp.ts +++ b/src/documentation/inlineHelp.ts @@ -25,7 +25,7 @@ const completionDatabase: CompletionDatabase = { name: "delayr", category: "time", description: "Delay a function n times by t ms", - example: "delayr(50, 3, () => beat(1) :: log('delayed'))", + example: "delayr(50,3,()=> beat(1)::log('hey!'))", }, toss: { name: "toss", @@ -37,7 +37,7 @@ const completionDatabase: CompletionDatabase = { name: "lpadsr", category: "synthesis", description: "Lowpass filter ADSR envelope", - example: "sound('sawtooth').lpadsr(2, 0, .1, 0, 0).out()", + example: "sound('sawtooth').lpadsr(2,0,.1,0,0).out()", }, lpenv: { name: "lpenv", @@ -987,7 +987,7 @@ export const toposCompletions = (context: CompletionContext) => { div.innerHTML = `

${completionDatabase[key].name} [${completionDatabase[key].category}]

${completionDatabase[key].description}

-
${completionDatabase[key].example}
+
${completionDatabase[key].example}
` div.classList.add("px-4", "py-2", "rounded-lg", "w-92"); return div From 32368f73cad32e9fb609b397954b22c9afcf388d Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 29 Oct 2023 23:09:09 +0100 Subject: [PATCH 4/7] Add option to turn it on/off --- index.html | 4 ++++ src/DomElements.ts | 15 ++++++++------- src/EditorSetup.ts | 5 +++-- src/FileManagement.ts | 9 ++++++++- src/InterfaceLogic.ts | 15 +++++++++++++++ src/documentation/inlineHelp.ts | 1 - src/main.ts | 1 + 7 files changed, 39 insertions(+), 11 deletions(-) diff --git a/index.html b/index.html index 16fc35f..8ecd55f 100644 --- a/index.html +++ b/index.html @@ -261,6 +261,10 @@ +
+ + +
diff --git a/src/DomElements.ts b/src/DomElements.ts index de5b0ce..964711c 100644 --- a/src/DomElements.ts +++ b/src/DomElements.ts @@ -1,12 +1,12 @@ export type ElementMap = { [key: string]: - | HTMLElement - | HTMLButtonElement - | HTMLDivElement - | HTMLInputElement - | HTMLSelectElement - | HTMLCanvasElement - | HTMLFormElement; + | HTMLElement + | HTMLButtonElement + | HTMLDivElement + | HTMLInputElement + | HTMLSelectElement + | HTMLCanvasElement + | HTMLFormElement; }; export const singleElements = { @@ -36,6 +36,7 @@ export const singleElements = { line_numbers_checkbox: "show-line-numbers", time_position_checkbox: "show-time-position", tips_checkbox: "show-tips", + completion_checkbox: "show-completions", midi_clock_checkbox: "send-midi-clock", midi_channels_scripts: "midi-channels-scripts", midi_clock_ppqn: "midi-clock-ppqn-input", diff --git a/src/EditorSetup.ts b/src/EditorSetup.ts index 3b22b16..5e3be76 100644 --- a/src/EditorSetup.ts +++ b/src/EditorSetup.ts @@ -37,7 +37,7 @@ import { inlineHoveringTips } from "./documentation/inlineHelp"; import { toposCompletions } from "./documentation/inlineHelp"; import { javascriptLanguage } from "@codemirror/lang-javascript" -const jsCompletions = javascriptLanguage.data.of({ +export const jsCompletions = javascriptLanguage.data.of({ autocomplete: toposCompletions }) @@ -53,7 +53,6 @@ export const editorSetup: Extension = (() => [ bracketMatching(), closeBrackets(), autocompletion(), - jsCompletions, highlightActiveLine(), highlightSelectionMatches(), keymap.of([ @@ -67,6 +66,7 @@ export const editorSetup: Extension = (() => [ export const installEditor = (app: Editor) => { app.vimModeCompartment = new Compartment(); app.hoveringCompartment = new Compartment(); + app.completionsCompartment = new Compartment(); app.withLineNumbers = new Compartment(); app.chosenLanguage = new Compartment(); app.fontSize = new Compartment(); @@ -91,6 +91,7 @@ export const installEditor = (app: Editor) => { app.withLineNumbers.of(lines), app.fontSize.of(fontModif), app.hoveringCompartment.of(app.settings.tips ? inlineHoveringTips : []), + app.completionsCompartment.of(app.settings.completions ? jsCompletions : []), editorSetup, toposTheme, app.chosenLanguage.of(javascript()), diff --git a/src/FileManagement.ts b/src/FileManagement.ts index 75db8a6..6b8b969 100644 --- a/src/FileManagement.ts +++ b/src/FileManagement.ts @@ -48,6 +48,7 @@ export interface Settings { * @param line_numbers - Whether or not to show line numbers * @param time_position - Whether or not to show time position * @param tips - Whether or not to show tips + * @param completions- Whether or not to show completions * @param send_clock - Whether or not to send midi clock * @param midi_channels_scripts - Whether midi input channels fires scripts * @param midi_clock_input - The name of the midi clock input @@ -64,6 +65,7 @@ export interface Settings { time_position: boolean; load_demo_songs: boolean; tips: boolean; + completions: boolean; send_clock: boolean; midi_channels_scripts: boolean; midi_clock_input: string | undefined; @@ -125,6 +127,7 @@ export class AppSettings { * @param line_numbers - Whether or not to show line numbers * @param time_position - Whether or not to show time position * @param tips - Whether or not to show tips + * @param completions - Whether or not to show completions * @param send_clock - Whether or not to send midi clock * @param midi_channels_scripts - Whether midi input channels fires scripts * @param midi_clock_input - The name of the midi clock input @@ -140,7 +143,8 @@ export class AppSettings { public selected_universe: string = "Default"; public line_numbers: boolean = true; public time_position: boolean = true; - public tips: boolean = true; + public tips: boolean = false; + public completions: boolean = false; public send_clock: boolean = false; public midi_channels_scripts: boolean = true; public midi_clock_input: string | undefined = undefined; @@ -164,6 +168,7 @@ export class AppSettings { this.line_numbers = settingsFromStorage.line_numbers; this.time_position = settingsFromStorage.time_position; this.tips = settingsFromStorage.tips; + this.completions = settingsFromStorage.completions; this.send_clock = settingsFromStorage.send_clock; this.midi_channels_scripts = settingsFromStorage.midi_channels_scripts; this.midi_clock_input = settingsFromStorage.midi_clock_input; @@ -193,6 +198,7 @@ export class AppSettings { line_numbers: this.line_numbers, time_position: this.time_position, tips: this.tips, + completions: this.completions, send_clock: this.send_clock, midi_channels_scripts: this.midi_channels_scripts, midi_clock_input: this.midi_clock_input, @@ -220,6 +226,7 @@ export class AppSettings { this.line_numbers = settings.line_numbers; this.time_position = settings.time_position; this.tips = settings.tips; + this.completions = settings.completions; this.send_clock = settings.send_clock; this.midi_channels_scripts = settings.midi_channels_scripts; this.midi_clock_input = settings.midi_clock_input; diff --git a/src/InterfaceLogic.ts b/src/InterfaceLogic.ts index 1cfd385..18bb17d 100644 --- a/src/InterfaceLogic.ts +++ b/src/InterfaceLogic.ts @@ -21,6 +21,7 @@ import { loadSamples } from "./API"; import { tryEvaluate } from "./Evaluator"; import { inlineHoveringTips } from "./documentation/inlineHelp"; import { lineNumbers } from "@codemirror/view"; +import { jsCompletions } from "./EditorSetup"; export const installInterfaceLogic = (app: Editor) => { (app.interface.line_numbers_checkbox as HTMLInputElement).checked = @@ -28,6 +29,8 @@ export const installInterfaceLogic = (app: Editor) => { (app.interface.time_position_checkbox as HTMLInputElement).checked = app.settings.time_position; (app.interface.tips_checkbox as HTMLInputElement).checked = app.settings.tips; + (app.interface.completion_checkbox as HTMLInputElement).checked = app.settings.completions; + (app.interface.midi_clock_checkbox as HTMLInputElement).checked = app.settings.send_clock; (app.interface.midi_channels_scripts as HTMLInputElement).checked = @@ -378,6 +381,18 @@ export const installInterfaceLogic = (app: Editor) => { }); }); + app.interface.completion_checkbox.addEventListener("change", () => { + let checked = (app.interface.completion_checkbox as HTMLInputElement).checked + ? true + : false; + app.settings.completions = checked; + app.view.dispatch({ + effects: app.completionsCompartment.reconfigure( + checked ? jsCompletions : [] + ), + }); + }); + app.interface.midi_clock_checkbox.addEventListener("change", () => { let checked = (app.interface.midi_clock_checkbox as HTMLInputElement) .checked diff --git a/src/documentation/inlineHelp.ts b/src/documentation/inlineHelp.ts index 658c806..eddb2c3 100644 --- a/src/documentation/inlineHelp.ts +++ b/src/documentation/inlineHelp.ts @@ -971,7 +971,6 @@ export const inlineHoveringTips = hoverTooltip( } ); - export const toposCompletions = (context: CompletionContext) => { let word = context.matchBefore(/\w*/) if (word) { diff --git a/src/main.ts b/src/main.ts index d1c8203..1cdff38 100644 --- a/src/main.ts +++ b/src/main.ts @@ -42,6 +42,7 @@ export class Editor { withLineNumbers!: Compartment; vimModeCompartment!: Compartment; hoveringCompartment!: Compartment; + completionsCompartment!: Compartment; chosenLanguage!: Compartment; dynamicPlugins!: Compartment; currentDocumentationPane: string = "introduction"; From c472fd1dfc6b8968f3015ea3963e9322a3797ad6 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 31 Oct 2023 01:08:31 +0100 Subject: [PATCH 5/7] augment default fftsize --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index d1c8203..11570a3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -64,7 +64,7 @@ export class Editor { color: "#fdba74", thickness: 4, refresh: 1, - fftSize: 256, + fftSize: 1024, orientation: "horizontal", is3D: false, size: 1, From 164e8ea9da42ef6e0605f7a747eb5ee5f425742b Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 31 Oct 2023 01:20:04 +0100 Subject: [PATCH 6/7] better smearing --- src/AudioVisualisation.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/AudioVisualisation.ts b/src/AudioVisualisation.ts index c5dddc0..9778bf6 100644 --- a/src/AudioVisualisation.ts +++ b/src/AudioVisualisation.ts @@ -163,6 +163,8 @@ export const runOscilloscope = ( } analyzer.getFloatTimeDomainData(dataArray); + canvasCtx.globalCompositeOperation = 'source-over'; + canvasCtx.fillStyle = "rgba(0, 0, 0, 0)"; canvasCtx.fillRect(0, 0, WIDTH, HEIGHT); @@ -179,6 +181,9 @@ export const runOscilloscope = ( } else { canvasCtx.strokeStyle = app.osc.color; } + const remainingRefreshTime = app.clock.time_position.pulse % app.osc.refresh; + const opacityRatio = 1 - (remainingRefreshTime / app.osc.refresh); + canvasCtx.globalAlpha = opacityRatio; canvasCtx.beginPath(); // Drawing logic varies based on orientation and 3D setting @@ -213,6 +218,7 @@ export const runOscilloscope = ( } canvasCtx.stroke(); + canvasCtx.globalAlpha = 1.0; // Reset globalAlpha to default } draw(); }; From 370624e4bfeccf6688417c25b545aeed79f491ab Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 31 Oct 2023 01:43:18 +0100 Subject: [PATCH 7/7] cooler scope --- src/AudioVisualisation.ts | 43 ++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/AudioVisualisation.ts b/src/AudioVisualisation.ts index 9778bf6..dc0b06b 100644 --- a/src/AudioVisualisation.ts +++ b/src/AudioVisualisation.ts @@ -125,6 +125,8 @@ export interface OscilloscopeConfig { size: number; } +let lastZeroCrossingType: string | null = null; // 'negToPos' or 'posToNeg' + /** * Initializes and runs an oscilloscope using an AnalyzerNode. * @param {HTMLCanvasElement} canvas - The canvas element to draw the oscilloscope. @@ -186,39 +188,60 @@ export const runOscilloscope = ( canvasCtx.globalAlpha = opacityRatio; canvasCtx.beginPath(); - // Drawing logic varies based on orientation and 3D setting + + let startIndex = 0; + for (let i = 1; i < dataArray.length; ++i) { + let currentType = null; + if (dataArray[i] >= 0 && dataArray[i - 1] < 0) { + currentType = 'negToPos'; + } else if (dataArray[i] < 0 && dataArray[i - 1] >= 0) { + currentType = 'posToNeg'; + } + + if (currentType) { + if (lastZeroCrossingType === null || currentType === lastZeroCrossingType) { + startIndex = i; + lastZeroCrossingType = currentType; + break; + } + } + } + + if (app.osc.is3D) { - for (let i = 0; i < dataArray.length; i += 2) { + for (let i = startIndex; i < dataArray.length; i += 2) { const x = (dataArray[i] * WIDTH * app.osc.size) / 2 + WIDTH / 4; const y = (dataArray[i + 1] * HEIGHT * app.osc.size) / 2 + HEIGHT / 4; - i === 0 ? canvasCtx.moveTo(x, y) : canvasCtx.lineTo(x, y); + i === startIndex ? canvasCtx.moveTo(x, y) : canvasCtx.lineTo(x, y); } } else if (app.osc.orientation === "horizontal") { - let x = 0; const sliceWidth = (WIDTH * 1.0) / dataArray.length; const yOffset = HEIGHT / 4; - for (let i = 0; i < dataArray.length; i++) { + let x = 0; + for (let i = startIndex; i < dataArray.length; i++) { const v = dataArray[i] * 0.5 * HEIGHT * app.osc.size; const y = v + yOffset; - i === 0 ? canvasCtx.moveTo(x, y) : canvasCtx.lineTo(x, y); + i === startIndex ? canvasCtx.moveTo(x, y) : canvasCtx.lineTo(x, y); x += sliceWidth; } canvasCtx.lineTo(WIDTH, yOffset); } else { - let y = 0; const sliceHeight = (HEIGHT * 1.0) / dataArray.length; const xOffset = WIDTH / 4; - for (let i = 0; i < dataArray.length; i++) { + let y = 0; + for (let i = startIndex; i < dataArray.length; i++) { const v = dataArray[i] * 0.5 * WIDTH * app.osc.size; const x = v + xOffset; - i === 0 ? canvasCtx.moveTo(x, y) : canvasCtx.lineTo(x, y); + i === startIndex ? canvasCtx.moveTo(x, y) : canvasCtx.lineTo(x, y); y += sliceHeight; } canvasCtx.lineTo(xOffset, HEIGHT); } canvasCtx.stroke(); - canvasCtx.globalAlpha = 1.0; // Reset globalAlpha to default + canvasCtx.globalAlpha = 1.0; } + + draw(); };