01Charts谱面
Chart and input design谱面与输入设计
Twenty-one levels, all open: four classic modes with four levels each, plus five BACK PORCH SCAT tiers. The song tiers use keys; the original CHOP and TRIO also support clapping.共 21 关,全部开放:四种原有玩法各四关,加上 BACK PORCH SCAT 五档。新歌五档使用按键,原有 CHOP 和 TRIO 也支持拍手。
COPYCAT CLUB runs on the FoloToy AI Passport: ESP32-C3, 8 MB flash, a 240×320 RGB565 screen, three keys, a speaker and a microphone. The three keys share one ADC ladder, which recognises one held key at a time. CHOP and TRIO accept either UP or OK for a hit, allowing two-finger alternation. CHOIR maps high notes to UP and low notes to OK.COPYCAT CLUB 运行在 FoloToy AI Passport 上:ESP32-C3、8 MB flash、240×320 RGB565 屏幕、三个按键、一个喇叭和一个麦克风。三个键共用一路 ADC 分压电路,同一时刻识别一个按住的键。CHOP 和 TRIO 的目标拍可用 UP 或 OK 命中,支持双指交替;CHOIR 用 UP 对应高音、OK 对应低音。

| Mode玩法 | Rule规则 | BPM · I–IV |
|---|---|---|
| CHOP | Repeat the call's rhythm in the next bar.在下一小节重复示范节奏。 | 96 / 100 / 104 / 108 |
| TRIO | Hear two hits; add the equally spaced third.听两次示范,补上等间距的第三拍。 | 92 / 96 / 100 / 104 |
| CHOIR | Repeat high and low notes on two keys.用两个键重复高低音。 | 90 / 94 / 98 / 102 |
| REMIX | Sequence all three rules; IV changes mode each phrase.串联三种规则,IV 每个 phrase 切换玩法。 | 108 / 112 / 116 / 120 |
The sixteen classic charts use a 16-step bar. Level I requires at least 400 ms between hits, at most four hits per bar and at least 60% on-beat hits. Levels II–IV require at least two steps and 250 ms between hits, with at most six hits per bar. Repeated same-key targets require at least four steps and 400 ms.原有 16 关的谱面每小节分为 16 格。I 关要求拍点间距至少 400 ms,每小节最多四拍,正拍占比至少 60%。II–IV 关要求拍点间距至少两格且至少 250 ms,每小节最多六拍。同键连续目标至少间隔四格且至少 400 ms。
II and III use even-numbered steps, with minimum on-beat proportions of 50% and 30%. IV adds odd-numbered steps and a 25% minimum. Its patterns include tresillo and clave; TRIO uses changing gaps and cross-bar groups. The sixteen classic levels contain 654 target hits. BACK PORCH SCAT adds EASY, NORMAL and HARD at 112 BPM (37, 76 and 122 targets), plus BOOGALOO and REMIX at 120 BPM (94 and 82 targets). REMIX switches between CHOP, TRIO and CHOIR. The total is 1,065 targets across 21 levels; SONGS opens the five tiers from the navy home menu.II、III 关使用偶数格,正拍占比分别至少为 50% 和 30%。IV 开放奇数格,正拍占比至少为 25%,采用 tresillo、clave 等节奏型;TRIO 加入变化间距和跨小节组合。原有 16 关共包含 654 个目标拍。BACK PORCH SCAT 的 EASY、NORMAL、HARD 为 112 BPM(37、76、122 个目标拍),BOOGALOO、REMIX 为 120 BPM(94、82 个目标拍)。REMIX 在 CHOP、TRIO、CHOIR 之间切换。21 关共 1,065 个目标拍;深蓝色主菜单的 SONGS 入口打开新歌五档。
02Timing时序
Audio-clock judging音频时钟判定
Input timestamps map to the audio sample being played. Host simulation measures a maximum mapping error of 0.59 ms.输入时间戳映射到正在播放的音频采样位置。Host 模拟测得最大映射误差为 0.59 ms。
Audio is 16 kHz, 16-bit mono: 62.5 µs per sample. The synthesis task writes 256-sample blocks; DMA holds 4 × 128 samples, or 32 ms of audio. Each successful write publishes a sample count and its timestamp. The clock mapper keeps the last 32 distinct observations and takes the lower envelope of timestamp minus sample time.音频为 16 kHz、16 位单声道,每个采样 62.5 µs。合成任务每次写入 256 个采样;DMA 容纳 4 × 128 个采样,即 32 ms 音频。每次写入成功后发布采样计数及对应时间戳。时钟映射保留最近 32 次不同写入的观测,取「时间戳减采样时间」的下包络。
- Key callback timestamp按键 callback 时间戳
- Audio-clock mapping音频时钟映射
- Input calibration输入校准
- Nearest target最近目标拍
The key callback timestamps each event before queueing it. Mapping subtracts buffered samples to estimate the playback position. Song start uses a request/acknowledgement handshake: the audio task reports the sample where synthesis starts. Rendering uses a separate 12 ms visual lead; judging uses the mapped input position.按键 callback 在事件入队前记录时间戳。映射时扣除缓冲中的采样数,估计实际播放位置。曲目起点通过请求与回执确定:音频任务返回开始合成时的采样号。渲染单独使用 12 ms 的视觉提前量,判定使用映射后的输入位置。
| Measurement测量 | Conditions条件 | Result结果 |
|---|---|---|
| Host mapping errorHost 映射误差 | 4,000 write blocks, 0–3 ms scheduling jitter; after initial convergence4,000 个写块,0–3 ms 调度抖动;初始收敛后统计 | max 0.59 ms最大 0.59 ms |
| Historical device mapping spread历史真机 mapping spread | Key-to-audio-sample mapping over 5 s, debug autoplaydebug autoplay,5 s 内按键到音频采样的映射波动范围 | ≤0.12 ms |
Judging selects the nearest unjudged target with a matching key within ±130 ms. An absolute offset of at most 60 ms is JUST; the rest of the window is OK. Key and clap calibration each collect ten events, discard the first two and use the upper median of the remaining eight, clamped to ±250 ms.判定在 ±130 ms 内选择按键匹配、尚未判定且距离最近的目标。绝对偏移不超过 60 ms 计为 JUST,窗口内其余命中计为 OK。按键和拍手分别校准:采集十次,丢弃前两次,取剩余八次的上中位数,范围限制在 ±250 ms。
−130 ms0+130 ms
score = clamp(
trunc((100 × JUST + 70 × OK − 50 × STRAY) / targets),
0, 100
)
All targets judged JUST with zero STRAY yields PERFECT. Otherwise, scores of at least 85 yield SUPERB, at least 60 yield OK, and lower scores yield TRY.全部目标为 JUST 且 STRAY 为零时,评级为 PERFECT。其余成绩中,分数至少 85 为 SUPERB,至少 60 为 OK,更低为 TRY。
03Signal processing信号处理
Clap detection and echo model拍手检测与 echo model
An energy-domain echo model compares microphone onsets with the known speaker output. CLAP supports CHOP and TRIO at all four levels.能量域 echo model 将麦克风起音与已知喇叭输出比较。CHOP 和 TRIO 的四个关卡均支持 CLAP。
Both output and microphone signals pass through a second-difference high-pass filter. Energy is accumulated over 16 samples, giving 1 ms blocks. A 512-block output history supplies the reference. After alignment by loopback delay L, the maximum of four nearby reference blocks is multiplied by a Q12 gain. The predicted echo tail retains three quarters of its energy each millisecond.输出信号和麦克风信号都经过二阶差分高通。每 16 个采样累加一次能量,形成 1 ms 的块。参考来自最近 512 块输出能量。按 loopback 延迟 L 对齐后,取邻近四块中的最大值,乘以 Q12 增益;预测回声尾部的能量每毫秒保留四分之三。
high_pass = (x − 2 × z1 + z2) >> 4
mic_energy > max(
2000,
3 × predicted_echo,
8 × noise_floor,
6 × energy_2_to_3_ms_earlier
)
Detection uses a 120 ms refractory period and two subsequent blocks for peak confirmation. The onset is the first sample reaching 30% of the peak, then corrected by L. Adaptive gain accepts upward ratios below four times the current gain; upward updates use 1/64 and downward updates use 1/1024.检测设置 120 ms 不应期,并用后续两块确认峰值。起音定位到首次达到峰值 30% 的采样,再扣除 L。增益自适应仅接受低于当前增益四倍的向上估计;向上更新步长为 1/64,向下为 1/1024。
Loopback measurementLoopback 测量
- 3 ms startup ping开机 3 ms ping
- Count-in measurements数拍阶段测量
- Music onset correlation音乐起音互相关
Startup searches delays from 32 to 4,096 samples. Count-in updates use the median of three measurements that agree within 1 ms. During play, log-energy onset correlation checks 256 candidate lags every 512 ms. The strongest peak must exceed 20,000 and be at least 1.5 times the next candidate. Ping and count-in estimates are accepted within 40 samples of the music estimate.开机时在 32–4,096 个采样的范围内搜索延迟。数拍阶段取三次测量的中位数,三次结果相差须在 1 ms 内。游戏中每 512 ms 通过 log-energy 起音互相关检查 256 个候选 lag。最强峰须超过 20,000,且至少为次强候选峰的 1.5 倍。Ping 和数拍估计与音乐估计相差不超过 40 个采样时才被接受。
CLAP arrangementCLAP 编曲
During player-response bars, CLAP mutes hat, rim, crash, fill and kick click, low-passes the snare and sets the bass attack cutoff to 4 kHz. Call cues remain audible. This reduces high-frequency reference energy during the player's hits.在玩家回应的小节中,CLAP 将 hat、rim、crash、fill 和 kick click 静音,对 snare 做低通,并将 bass 起音 cutoff 设为 4 kHz。示范音保持可听。这降低了玩家拍点处的高频参考能量。
| Host simulationHost 模拟 | Detected / targets检出 / 目标 | Extra detections额外检测 | Max onset error最大起音误差 |
|---|---|---|---|
| CHOP IV | 50 / 50 | 0 | 0.31 ms |
| TRIO IV | 29 / 29 | 0 | 0.19 ms |
v7 host simulation, each level with clean and clipping echo. Simulated loopback delays of 700, 1,100 and 2,400 samples are measured as 701, 1,101 and 2,401.v7 host 模拟,每关分别使用 clean 和 clipping echo。设定为 700、1,100、2,400 个采样的 loopback 延迟,分别测得 701、1,101、2,401。
04Graphics画面
Strip renderingStrip 渲染
Two 40-row buffers use 50 KiB. CPU drawing and SPI DMA transfer overlap.两块 40 行 buffer 共占 50 KiB。CPU 绘制与 SPI DMA 传输并行进行。
CPU draws绘制 DMA sends发送
Each frame updates game state once, then replays rendering for every strip. Portrait uses eight strips and landscape six. Each buffer is sized for 320 × 40 × 2 bytes. A counting semaphore tracks buffer ownership; DMA completion returns a token.每帧更新一次游戏状态,再为各个 strip 重放渲染。竖屏使用八条,横屏使用六条。每块 buffer 按 320 × 40 × 2 字节分配。Counting semaphore 管理 buffer 归属,DMA 完成后归还 token。
Rotation waits for both DMA transfers to finish, then uses the display's MADCTL register to select orientation. Key labels and adjustment directions follow the physical key positions.转屏先等待两块 buffer 的 DMA 传输完成,再通过屏幕的 MADCTL 寄存器选择方向。按键标签和调节方向随物理按键位置调整。
Geometry uses Q16.16 coordinates. Scanlines separate fully covered spans, filled directly, from edge spans that evaluate SDF coverage. RGB565 blending processes the colour channels in parallel. Sine uses a 1,024-entry table; springs integrate with sub-steps of at most 4 ms.几何使用 Q16.16 坐标。每条扫描线区分完全覆盖区与边缘区:前者直接填色,后者计算 SDF coverage。RGB565 混色并行处理颜色通道。正弦采用 1,024 项查找表,spring 积分的子步长最多为 4 ms。
| Text layer文字层 | Representation表示方式 | Use用途 |
|---|---|---|
| micro | IBM Plex Mono · 11 px bitmap | Small labels小字号标签 |
| display | Archivo Black · 40 px SDF | Scalable titles可缩放标题 |
| giant | 1,621 scanline runs1,621 段扫描线游程 | Half-screen-height letters; cache rebuilt on rotation半屏高大字;转屏时重建 cache |
The cat artwork is rasterised into antialiased scanline runs: 5,076 B for the large cat and 1,584 B for the menu icon. The large cache is released after its morph animation.猫头图形预先光栅化为带抗锯齿的扫描线游程:大猫占 5,076 B,菜单图标占 1,584 B。大猫的 cache 在 morph 动画完成后释放。
05Validation验证
Frame and hint checks逐帧与提示档检查
The host runs the same C game code. The v7 snapshot checks 388,246 layout frames and 444,412 frames per flash level.Host 运行同一份 C 游戏代码。v7 快照检查了 388,246 帧版式,以及每个闪光档位各 444,412 帧画面。
The test driver supplies an explicit clock, timestamped input, an output-to-microphone model and in-memory saves. It calls the game's own init, input, audio, update and render functions through complete playthroughs, including missed hits, wrong keys and timing offsets.测试驱动提供显式时钟、带时间戳的输入、输出到麦克风的模型和内存存档。测试调用游戏自己的 init、input、audio、update、render 函数,完成整曲流程,覆盖漏拍、错键和时间偏移。
Per-frame luminance limits逐帧亮度变化阈值
NORMAL and REDUCED use project-defined limits on per-frame luminance change. Host checks decode RGB565 through sRGB to relative luminance, measure adjacent-frame mean changes and detect large-area reversals over one to six frames. The v7 flash matrix runs portrait and landscape.NORMAL 和 REDUCED 采用项目定义的逐帧亮度变化阈值。Host 检查将 RGB565 经 sRGB 解码为相对亮度,测量相邻帧平均亮度变化,并检测一至六帧内的大面积亮度往返变化。v7 闪光测试矩阵覆盖竖屏和横屏。
| Flash level闪光档位 | Mean-change limit平均变化上限 | Measured maximum实测最大值 | Own-gate events自身阈值下事件数 |
|---|---|---|---|
| NORMAL | 0.25 | 0.189 | 0 |
| REDUCED | 0.10 | 0.098 | 0 |
NORMAL allows zero reversal events affecting more than half the pixels at Δ > 0.3. REDUCED uses a quarter of the pixels at Δ > 0.2, allowing at most three events in any second. REMIX transitions cover the old scene before revealing the next, with iris radius proportional to √progress. REDUCED fades dense shapes toward the background during the 220 ms before a transition.NORMAL 要求「超过半数像素、Δ > 0.3」的往返事件为零。REDUCED 使用「超过四分之一像素、Δ > 0.2」的阈值,任意一秒内最多允许三次。REMIX 转场先遮住旧场景,再显示新场景,iris 半径与 √progress 成正比。REDUCED 在转场前 220 ms 将密集图形渐隐至背景色。
Layout probes版式 probe
Rendering records visible text and rule-line boxes. Pairwise AABB checks detect intersections; corner checks enforce a 30 px rounded safe area with a 2 px inset. The v7 layout pass reports zero issues across 388,246 checked frames in three orientations and two flash levels; gameplay is sampled every third frame.渲染时记录可见文字和分隔线的边界框。逐对 AABB 检查检测相交,角点检查约束半径 30 px、内缩 2 px 的圆角安全区。v7 版式测试在三种方向、两个闪光档位下检查 388,246 帧,报告零问题;游戏段每三帧检查一次。
Hint invariance提示档一致性
FULL previews calls and answers; LIGHT previews calls; EARS retains the beat grid, playhead and hit feedback. A host matrix replays sixteen levels × three hint levels × two key-input scripts. PCM hashes, ratings, counts and per-hit judgement data match across hint levels. Separate cue checks derive every target from the preceding call or TRIO's equal spacing.FULL 提前显示示范与回答,LIGHT 提前显示示范,EARS 保留节拍网格、播放指针和命中反馈。Host 矩阵重放 16 关 × 三个提示档 × 两种按键输入脚本,各提示档的 PCM hash、评级、计数和逐拍判定数据一致。另有示范充分性检查,确认每个目标都能由前面的示范或 TRIO 的等间距规则推导。
06Protocol协议
HMAC-authenticated score code经 HMAC 认证的成绩码
A 29-byte payload carries the result and an HMAC tag. The phone transfers the offline card's score to the leaderboard.29 字节 payload 包含成绩和 HMAC tag。手机将离线卡片的成绩传至排行榜。
- Card result卡片成绩
- HMAC-authenticated score code经 HMAC 认证的成绩码
- Phone scan + nickname手机扫码并填写昵称
- Server validation服务器校验
The body contains the version, stage, rating/hint/CLAP flags, score, JUST/OK/MISS/STRAY counts, an eight-byte random save identifier and a four-byte counter. HMAC-SHA256 authenticates the body with a shared symmetric key; the tag is truncated to eight bytes. C writes each field explicitly, and TypeScript reads multi-byte fields as little-endian through DataView.正文包含版本、关卡、评级/提示档/CLAP 标记、分数、JUST/OK/MISS/STRAY 计数、八字节随机存档标识和四字节计数器。HMAC-SHA256 使用共享对称密钥认证正文,tag 截取八字节。C 逐字段写入,TypeScript 通过 DataView 按 little-endian 读取多字节字段。
Base32 uses A–Z and 2–7. The uppercase URL uses QR alphanumeric encoding. At the published domain length, the result is QR version 4: 33 × 33 modules, 5 px per module and a four-module quiet zone. Reopening the same result reuses its code; the counter advances when a new result first opens RANK.Base32 使用 A–Z 和 2–7。大写 URL 采用 QR alphanumeric 编码。在已发布域名的长度下,生成 QR version 4:33 × 33 个模块,每模块 5 px,quiet zone 为四个模块。同一成绩重复打开时复用原码,新成绩首次打开 RANK 时计数器递增。
| Check检查 | Server behaviour服务器行为 |
|---|---|
| HMAC | Compare the authentication tag in constant time.以常量时间比较认证 tag。 |
| Rules规则 | Check target counts, recompute score and rating, validate stage and flags, and accept CLAP for CHOP/TRIO.核对目标计数,重算分数与评级,校验关卡和标记;CHOP/TRIO 接受 CLAP。 |
| Replay重放 | Atomically claim the save-identifier/counter pair with a create-only record.通过 create-only 记录,原子占用存档标识与计数器的组合。 |
Protocol tests reject all 232 single-bit mutations of the 29-byte payload. C and TypeScript share three test vectors. The server checks all 21 stages against the game charts, including BACK PORCH SCAT target counts of 37, 76, 122, 94 and 82. The five song tiers reject CLAP flags.协议测试拒绝 29 字节 payload 的全部 232 种单比特翻转。C 与 TypeScript 共用三组测试向量。服务器根据游戏谱面核对全部 21 关,BACK PORCH SCAT 五档的目标数依次为 37、76、122、94、82。新歌五档不接受 CLAP 标记。
07Persistence持久化
Submission recovery and saves提交恢复与存档
Persistent pending/done records preserve a submission across retries. Local saves keep best ratings, hint records and settings.持久化的 pending/done 记录在重试时保留原始提交。本地存档保存最好评级、提示档记录和设置。
- pending
- All-time + weekly boards总榜与周榜
- done
A pending record holds the original payload, first nickname, submission time and week. A retry with the same code completes writes using that record, then marks it done with compare-and-swap. Nickname updates require a counter at least as high as the entry's nickname counter. The file backend serialises reads and writes so content and version tags come from the same snapshot.Pending 记录保存原始 payload、首次昵称、提交时间和周次。同码重试使用这份记录补齐写入,再通过 compare-and-swap 标记为 done。更新昵称要求提交计数器不小于条目的昵称计数器。File backend 串行处理读写,保证内容和版本 tag 来自同一快照。
The v7 server test record contains 49 passing tests. Boards retain the best result per save and rank by score, then EARS → LIGHT → FULL, JUST count, STRAY count and submission time. CLAP appears as a badge. Weekly boards follow calendar weeks in UTC+8. The overall board sums each level’s best score, with all-time and weekly views; the stage directory groups the sixteen classic levels and five BACK PORCH SCAT tiers.v7 服务器测试记录为 49 项通过。每个榜单保留每份存档的最好成绩,依次按分数、EARS → LIGHT → FULL、JUST 数、STRAY 数和提交时间排序。CLAP 显示为徽章。周榜按 UTC+8 自然周划分。总榜将各关最好成绩相加,可切换历史与本周;关卡目录分别列出原有 16 关和 BACK PORCH SCAT 五档。
Local save format本地存档格式
| Data数据 | Size大小 | Layout布局 |
|---|---|---|
| Core save主存档 | 32 B | Fixed layout readable by v6; brightness uses a reserved byte.固定布局,v6 可读取;亮度使用一个预留字节。 |
| Level IV ratingsIV 关评级 | 8 B | Separate extension record.单独的扩展记录。 |
| Hint records提示档记录 | 8 B | Two bits per level.每关占两位。 |
These three classic-level save payloads total 48 B. The five BACK PORCH SCAT tiers store their best results separately. Compile-time size assertions fix their layouts; reads validate length and magic. Hint records advance when a level is cleared with OK or better using fewer hints. Result-page storage writes run after 2.6 s on the page and 1.5 s of inactivity.原有关卡的这三份存档 payload 共 48 B。BACK PORCH SCAT 五档另行保存各自的最好成绩。编译期尺寸断言固定布局,读取时校验长度和 magic。以 OK 或更高评级、用更少提示通关时,提示档记录才推进。结果页的存储写入安排在进入页面 2.6 s 且空闲 1.5 s 之后。
Installing an app or update from the FoloToy platform clears local best ratings, hint records and settings. v8 keeps all 21 levels open from the start. Leaderboard scores remain on the server.从 FoloToy 平台安装应用或更新会清空本地最好评级、提示档记录和设置。v8 的 21 关从一开始就全部开放,排行榜成绩保留在服务器上。
