ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
翻譯準備中
尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗A browser version of the Patch School lessons: patch modules, play them with the computer keys or a MIDI keyboard, and work through the lessons. The public copy lives in Modular-Synth-Web (Cloudflare Pages publishes it to school.bradleyejjackson.com); this folder is where the page is worked on.
index.html is the whole page, and sw.js lets it work offline. Copy both to Modular-Synth-Web to publish.tools/ regenerates the lessons part of the page from the plugin's own lessons:
LessonDump.cpp: build it as a temporary target in Tests/CMakeLists.txt, run it to write lessons.json, and LessonDump names to write names.json.webmods.json: the page's const MODULES = [...] as a dict by id. When the plugin gains modules, build tools/DumpModules.cpp the same way and run it to write mods.json, write each new module's what and try in tools/moduledesc.json, and run python3 tools/modules.py index.html mods.json to add their jacks and knobs to MODULES. Each one also needs an engine port in the page (an Object.assign(IMPL, {...}) block, per patch-school-src's CONTRACT.md); node tools/modtest.mjs index.html <id>... runs them for 3 s and prints their peaks and speed.python3 tools/gen.py <dump dir> writes course.js there. It leaves out Patch School Pro lessons (Level 2 on, "pro" in the dump): the web course is the free one. Steps about plugin features the lab doesn't have (custom scales, tunings) are in its PLUGIN_ONLY and become "in the synth" steps.index.html to <dir>/live.html, then python3 tools/update.py <dir> <dump dir>: it swaps in the new course and makes the page's hand edits (public wording, counts from the data, Part 7). Each edit is skipped when the page already has it. Bump PLUGIN_MODULES in it when the plugin gains modules.tools/PresetDump.cpp the same way and run it to write presets.json, then node tools/labtypes.mjs index.html > labtypes.json and python3 tools/presets.py index.html presets.json labtypes.json. It puts every factory preset the lab can play into the page's LAB_PRESETS (the lab's Presets menu and the Presets tab's buttons). Re-run it when presets or the lab's modules change.python3 tools/help.py index.html ../Source/engine/ModuleHelp.cpp copies the plugin's tooltip words into the page's MODULE_HELP (Every module's knob lists and the lab's ? help, less the words about features the lab lacks: WEB_HELP in help.py), what each jack carries from kindRules in ModuleLibrary.cpp next to it, and each module's "In Eurorack" name and the glossary (GLOSSARY) from EurorackNames.cpp. Re-run it when ModuleHelp.cpp, EurorackNames.cpp, kindRules or the lab's modules change.node tools/peaks.mjs index.html plays every lab preset with a note held and lists any louder than 1.2 (or silent: the Audio In ones need a sound in).let __stash = null; window.__t = { LESSONS, freshRack, isTask, get lab() { return lab; }, LAB_TYPES, pickLesson, get song() { return song; }, songFlags, leaves, reset: (ls) => { closeTrack(true); if (ls.songFix) { if (!__stash) __stash = JSON.parse(JSON.stringify(song)); setSong(demoSong()); SONG_FIXES[ls.id].break(); songChanged(true); return; } if (__stash) { setSong(__stash); __stash = null; } if (ls.tab) stopSong(); if (!ls.tab) freshRack(ls.outAt); if (ls.start) runDiff(ls.start); } }; after const isTask in a test copy (a Song page fix-it runs on a fresh demo song it breaks, and the song the lessons built comes back after it), then node tools/check.mjs <test copy>: it takes every lesson in order, Song mode ones too, and every step's Show me must make its check pass (it prints nothing when they all do).WEB_STEPS in the page gives the step web words, a check and a Show me (keyed lesson id|step title), so all but two of Parts 5 and 6's steps run here (5.3's Ableton step, and 5.1's keyboard knob, which has a close version). A step that still needs the real synth can have close: { notes, build }, which adds a Hear a close version button: the step's patch plays notes ([note, start s, length s]), after build() adds a stand-in for what the plugin does. The web engine is a little louder than the plugin on a few presets: WEB_TRIM in presets.py turns them down. WEB_WORDS, next to WEB_STEPS, swaps plugin words for the lab's in every step (how to make a module shared, the Hold a note button); lesson intros, How it works and quizzes get theirs from update.py.Level 1 is free, so its tests are here too, run the way the plugin's lessons panel runs them. A challenge gives a goal and what the finished sound must do, instead of steps: Start challenge clears the rack and starts a clock, each hint costs one of 3 points, and Give up stops the clock and unlocks Show me on each goal (no score). A fix-it is a challenge that starts from a broken patch (start in its lesson, the dump's startPatch). Part 5's two Song page fix-its (5.8 a muted mix, 5.9 a sour bass; songFix in the course, the dump's songFixIt) break the demo song instead and leave the rack alone: SONG_FIXES in the page has each one's break() (it refuses unless the song is the demo, New song… > Demo song) and each goal's Show me, ported from the plugin's Part5Lessons.cpp. Part 2 ends with two challenges (2.11 a wavetable pad, 2.12 a struck chime) and three fix-its (2.13 the bell, 2.14 effects in the wrong order, 2.15 a reverb that should be shared and an envelope that shouldn't). Part 4 ends with six recipe challenges (4.19 to 4.24, the plugin's recipeChallengeLessons; 4.23 and 4.24 are optional like their recipes). The 80s lead's Arp goal takes the lab's Arpeggiator module, so WEB_GOAL_APPLY in gen.py gives its Show me. The goals' checks take any patch that does the job, so they aren't generated: GOALS in the page has each one, keyed lesson id|goal title, ported from the plugin's Source/lessons/Challenges.cpp (L.path-style: does a signal get from one module to another through any modules between). gen.py emits check: () => GOALS["c_bass|It is low"](), so a new goal there needs its entry in GOALS too. While a try runs, Ctrl+Z only undoes what you did in it.
An ear test plays mystery sounds (each of Level 1's nine has ten questions and asks five, drawn at random: served in the course, the plugin's Lesson::earServed, so a retake is a new test) (each question's choices have a sound each, as runDiff data built from MIDI In and Output); Play the sound builds it in the rack for a moment, holds its note, then puts your rack back. One guess per question, then every choice can be heard. Three of them play by themselves while the note is held, from a Clock whose Run is MIDI In's Gate: 3.14 rhythm (straight or swung, which division, Euclidean hits), 5.10 pitch (in tune, intervals, scales) and 5.11 mix balance (a kick, hats, bass and keys band with one part too loud, too quiet or panned). The Level 1 check is the plugin's: a build from a blank rack (its own goals), Next: the fix-it (one of mastery.fixIts drawn at random), Next: by ear (mastery.earQuestions drawn from its ear tests), all within mastery.minutes, then a score sheet. Best scores are kept as the app keeps them, results ({ id: [points, seconds] }) and checks ({ id: { built, fixed, heard, seconds, tries, passedOn } }), and progress sync keeps the best of each, as the app's Progress::merge does. A challenge or ear test counts as done once it has a score, and the check once it is passed.
Lesson 6.9, Score your track, ends Part 6 with a self-score sheet: Capstone A's eight lines, each a step with score (four levels, 0 to 3) instead of a check. Picking a level ticks the step; once all eight are scored the page says the total, whether it would pass Capstone A's mark (two thirds, with no zero) and the line to work on first (sheetVerdict(), the plugin's Lessons::sheetVerdict). The scores are kept in this browser only, as selfScores ({ id: [score or -1 per step] }), like the app's own (not synced).
Like the plugin's Patch page: Ctrl+wheel (or a trackpad pinch, or two fingers on a touch screen) zooms the rack between 25% and 200% around the mouse, and Ctrl+Plus, Ctrl+Minus and Ctrl+0 step and reset it while the mouse is over the rack. The rack is drawn at 100% and scaled with a CSS transform inside #rackSize, which is the zoomed size so the scroll bars cover it; module places, cables and the lasso stay in rack pixels (divide screen distances by zoom). The zoom bar (bottom-left: −, the percentage, +, Fit, Map) and the Rack menu have the same items. The map (bottom-right, hidden under 600 px) draws each module in its group's colour, the cables as lines and the view as a frame; click or drag it to move the view, and Map hides it (kept in the settings). Hold the middle button and drag anywhere to move the view (past the edge the rack grows); a middle-click without moving selects the cable under the mouse, or the cable of a plugged jack, which then glows white: Delete removes it and Escape lets it go. Right-clicking a plugged jack lists its cables to unplug (and Unplug all), and right-clicking a cable offers Select cable and Delete cable. An input that does something of its own when empty (normalled) has a dotted ring, and its tooltip's Unpatched: line says what (from normals in MODULE_HELP, which tools/help.py copies from the plugin's normalRules).
Search presets… at the top of the Patch menu, or Ctrl+P, opens a box that filters every preset and My patches as you type: each word must be in the name, what it is, or its group. Up and Down pick, Enter loads, Escape closes.
The lab keeps the rack and lesson progress in the browser's local storage, plus a list of named patches (My patches). Ctrl+Z and Ctrl+Shift+Z (or Ctrl+Y) undo and redo changes to the rack. Export file writes a patch as JSON ("kind": "modular-synth-web-patch", modules with their changed knobs, and cables as [from module, output, to module, input]); Import file reads one back. Copy share link puts the same JSON, deflated and base64url-encoded, after #patch= in the address. A Notes module's words go with it ("text"). Modules the lab doesn't have are left out when a patch is opened.
Like the plugin, modules can be folded into a block: one box with just the jacks and knobs you pick. Shift-click (or Ctrl-click) module titles, or drag a box around them on an empty part of the rack with the mouse, then press Fold into block… (Ctrl+G); its window also lists every module with a tick box, so it works on a phone or from the keyboard. MIDI In and Output stay outside. Dragging the title of one selected module moves them all. A block's + unfolds it, and save keeps it under My blocks in the Add module menu (local storage modular-synth-patch-school-blocks-v1; Manage My blocks… deletes them). In the rack a block is a module { type: 'block', block: { name, mods, cables, inputs, outputs, knobs } }: each input jack lists the ports inside its cable feeds, each output is one port inside, and each knob names a module inside and its knob. The modules inside keep their uids, and enginePatch unfolds every block (blocks inside blocks too) before the engine sees the patch, so a block sounds the same as its modules. In a patch file or share link a block is { "type": "block", "x", "y", "block": { "name", "mods", "cables", "inputs": [{ "label", "ports": [[module, port]] }], "outputs", "knobs": [{ "label", "mod", "param" }] } }, modules named by their place in the block's list; pages from before blocks skip them.
Above the rack is one row of buttons: Add module and its search, Undo and Redo, Tidy up, Clear rack (Ctrl+Z brings the patch back), the Rack menu (Fold into block…, Cables on top, the zoom items, Map, Load Init patch, Clear rack), the Patch menu (Search presets…, Presets, My patches, Save and Delete, Export file, Import file, Copy share link) and ?. The menus are <details class="menu"> elements: every button keeps its id, so the lessons' Show me and checks don't care which menu it sits in; a button inside closes its menu, a select keeps it open. Audio In hears and Sidechain In hears sit in a row of their own that paintSrc() shows only while the rack has an Audio In or Sidechain In module (or a source is already picked). The cable colour key and the drag hint sit under the rack.
The ? button above the rack is the plugin's module help: while it's lit, resting the mouse on a module's title, a knob or a jack (or Tabbing to a jack or knob) shows what it does or carries. The words come from MODULE_HELP, and what each jack carries from the plugin's kindRules (both copied in by tools/help.py). It starts off and is kept with the other settings.
The address names what to show: #lab, #chords, #song, #modules, #glossary or #presets for a tab, #lesson-4.6 for a lesson (picking a lesson puts it in the address, so Back goes to the one before), #module-filter for a card on Every module (the # by each module's name is its link), and #patch=... for a shared patch. Course map, under the lesson picker, opens with Your path (PATH and pathCard(), the plugin's Lessons::path()): the course as five stretches (Level 1 is Parts 1 to 3; Parts 4 to 7 put it to work, free; then Pro's Levels 2, 3 and 4), each with how far through it you are, links to its parts, and what ends it, with "you are here" on the stretch Continue would open. A Pro stretch's lesson count and minutes are copied from the plugin. Under it the map lists every part with its ticks, a done total (Part 4's optional recipes, 4.5 on, counted apart: optional: true in the course, from the plugin's Lesson::optional; isExtra() in the page makes Continue and Suggested next take them, and the workshops, last) and about how long it takes (each lesson's minutes come from lessonMinutes(), the same rule as the plugin's Lessons::minutesFor). Under the free parts it lists the Patch School Pro parts (PRO_PARTS: Levels 2 to 4, greyed, with their lesson counts) and a Learn about Pro link once PRO_URL is set. Once the Level 1 check is passed or 6.9 is done, a What's next card (whatsNext(), the plugin's Lessons::whatsNext less Today's five and Tune-up) shows under that lesson and at the end of the course map. Reset my progress clears the ticks and scores without touching the rack, My patches or songs. Once SYNC_URL is set (the licence server's address), the course map also offers Sign in with a Patch School Pro licence key: the ticks are then kept in that key's account too and stay in step with the app. The key and the last synced copy are kept in local storage modular-synth-patch-school-account; node tools/synctest.mjs index.html tests the merge rules.
The Glossary tab is the plugin's Help > Eurorack glossary: the words on hardware panels and in manuals (CV, gate, 1 V/oct, normalled, LPG, EOC...), then what each module is called on a Eurorack panel, grouped like Every module, with a search box. Each card on Every module and each module's ? help also has its "In Eurorack" line. Both come from the plugin's EurorackNames.cpp, which names generic kinds of module only, never a maker.
Lessons point into it. A word the plugin's lessons mark [g:gate] (Level 1 has them throughout) becomes <span class="g"> in gen.py (hand-written lessons use G('gates')): underlined in the accent, its entry shows when you rest the mouse on it or Tab to it, and a click or Enter opens it in the Glossary tab (Back returns to the lesson). glossaryFor() finds the entry the way the plugin's ModuleLibrary::findGlossary does (any case, a plural or "-ed" dropped, one of an entry's names, and GLOSSARY_ALIASES such as envelope for EG), so keep the two in step.
Each Part 4 recipe names the factory presets built the same way (the plugin's Lesson::presets, presets in the course): How it works ends with a "Presets like this" line, and the preset search (Ctrl+P) tags each of them with its lesson ("Lesson 4.1"); typing "lesson" lists them all. The core recipes (4.1 to 4.4) and 6.2 end with a Save its patch sheet step, which the lab marks as one for the synth, as it has no patch sheets.
The Song mode tab is a small copy of the standalone app's Song page: up to 8 tracks, each with 8 patterns (loops of 1, 2, 4 or 8 bars, notes in beats as [start, note, length, velocity]), a 128-bar arranger (a pattern run plays from its first bar and loops), a mixer with a Master level and meter, and 4 automation lanes a track. Each track has a Track editor like the plugin's: a piano roll (or a drum grid with the Drum Kit's names), grid, velocity, key with Show key and Snap to key, and chord pads with Triads/7ths and a Shape (Smooth keeps the voicing close to the chord before). The chord names of what's written show along the top. My songs keeps named songs in this browser, and Export WAV renders the song through fresh copies of the synths, named after the song.
Edit on a track opens its synth in the Patch lab with a banner saying so: every change to the rack goes back into the track, its loop is in the Track editor under the keyboard (Play loop plays it through the lab's synth), and Stop editing puts your own rack back. With no track open, that Track editor holds the lab's own loop. Notes in the song and the loop start on their exact sample. Lessons that start from an empty rack stop editing first.
The lab plays all of the plugin's 162 modules. Like the plugin, pitch is 1 V per octave and every other signal counts 1 as 5 V, and a cable keeps its volts: cableGain in the page scales a cable from any other kind of jack into a pitch jack by 5, and one out of a pitch jack by 1/5, reading which jacks are pitch from MODULE_HELP (so re-run tools/help.py when kindRules gains a pitch jack). The Slew works as the plugin's does (a share of the way each sample), so a pitch glides the same either way. The Sample Player records from its Rec In jacks while its Rec button is lit (or its Rec jack is high): the engine (Engine.record, like the plugin's Engine::feedRecorders) posts each finished take to the page as a take message, which loads it like a file (kept until the page is reloaded). The Multisample's panel picks a sound and a root note for each of its 8 zones. The Acid Sequencer's strip sets each step's note (drag it up or down, or use the arrow keys) and its On, Slide and Accent switches; the Spectrum draws its bars from the latest 1024 samples, and the XY Scope X against Y; a Notes module's words are kept with the patch. MIDI Out and CC Out send to a Web MIDI output picked on their panel (kept in this browser, with Nowhere to turn them off): another instrument, or a virtual cable into a DAW. Mute ends any notes MIDI Out left playing. The Arpeggiator plays the keys held on the lab's keyboard, newest last.
Jacks work from the keyboard: Tab to a jack and press Enter or Space to pick it, then Enter on a jack of the other kind connects them. Escape lets go, and Delete unplugs a focused jack. Every message also goes to a screen reader's live region, and each jack's label says what it's plugged into. The ☾/☀ button in the header switches between light and dark (it starts by following the computer), and Cables on top draws the cables over the modules instead of behind them; both are kept in this browser along with the Volume slider. The engine soft-limits its output, so Volume sets the loudest the lab can be, and the scope shows LIMIT when a patch is loud enough for the limiter to squash it.
On one column (a tablet or phone) the step you're on is repeated in a strip just above the rack's buttons, with Show me and Back to the lesson, and Start lesson scrolls to it. The toolbar wraps to two rows, and the Rack and Patch menus work the same at every width; under 600 px the keyboard shows one octave so the keys are about 40 px wide. On a touch screen the computer-key letters and their hint are hidden.
The page is installable (Add to Home Screen, or Install in Chrome and Edge): a script near the top makes the web app manifest as a blob and its icons as canvas PNGs, and the favicon is an inline SVG. All three, and the header logo, are the Patch School "P slate" mark in its free colours (dark tile, orange cable; the tile turns light in the dark theme). The header's "Patch School" wordmark is Outfit 600 turned into SVG paths, so the page loads no extra font. sw.js is the offline copy: a service worker that keeps the page and the Google fonts in a cache, fetching the page fresh when online and serving the cached one when not. Browsers only take a service worker from its own file, so publishing copies three files to Modular-Synth-Web: index.html, README.md and sw.js. Bump CACHE in sw.js only if its caching changes; page updates are picked up on the next online visit. Save for offline (in Lesson 1.1) also downloads the page as one file, which works with no internet since the page fetches nothing else.
Works in a recent Chrome, Edge, Firefox or Safari on a computer. Press Start sound first. A USB MIDI keyboard works in Chrome, Edge and Opera; Firefox asks for a site permission first; Safari has no Web MIDI.