Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b4f005c
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화
seonghobae Aug 9, 2026
ccbb9fe
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 trivy ignore 추가
seonghobae Aug 9, 2026
a922c37
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 trivy ignore/npm audit 의존성 픽스 추가
seonghobae Aug 9, 2026
93dd0f2
fix(security): remove obsolete CVE ignores
seonghobae Aug 11, 2026
c41e5fc
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 trivy ignore/npm audit 의존성 픽스 추가
seonghobae Aug 14, 2026
8730b85
chore(perf): isolate register-overlap optimization
seonghobae Aug 14, 2026
c4f0c58
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 보안 취약점 수정
seonghobae Aug 14, 2026
ab763c3
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 보안 취약점 수정
seonghobae Aug 14, 2026
6b34b09
chore(perf): restore isolated register-overlap slice
seonghobae Aug 14, 2026
5d5b940
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 보안 취약점 수정
seonghobae Aug 14, 2026
8b0ce9e
perf(overlap): restore register optimization to atomic scope
seonghobae Aug 14, 2026
20bdd39
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 보안 취약점 수정
seonghobae Aug 14, 2026
c955077
fix(scope): isolate active-stem overlap optimization
seonghobae Aug 14, 2026
b317318
test(perf): cover register-overlap resource guards
seonghobae Aug 14, 2026
cefa443
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 보안 취약점 수정
seonghobae Aug 14, 2026
36c821b
fix(perf): restore atomic register-overlap scope
seonghobae Aug 14, 2026
95ae2c5
test(overlap): preserve equal-severity band order
seonghobae Aug 15, 2026
930496c
fix(overlap): preserve pre-optimization tie order
seonghobae Aug 15, 2026
b1db384
docs(changelog): record register-overlap optimization
seonghobae Aug 15, 2026
11221cf
test(overlap): keep resource policy out of feature optimization
seonghobae Aug 16, 2026
7a3f566
fix(overlap): keep resource admission in canonical policy
seonghobae Aug 16, 2026
c5f2b49
test(overlap): reject fabricated silent-stem warnings
seonghobae Aug 16, 2026
b55e21c
fix(overlap): prevent threshold edge cases from fabricating warnings
seonghobae Aug 16, 2026
4c08619
test(overlap): cover boolean threshold fail-closed path
seonghobae Aug 16, 2026
171f0ab
docs(changelog): record overlap threshold fail-closed behavior
seonghobae Aug 16, 2026
a559d65
⚡ Bolt: O(1) 레지스터 중복 감지 루프 최적화 및 보안 취약점 수정
seonghobae Aug 16, 2026
05533f2
fix(overlap): restore isolated active-stem filter without local caps
cursoragent Aug 16, 2026
a41153f
feat(roles): wire measured register overlap into section warnings
cursoragent Aug 16, 2026
79f2a68
test(roles): reject invented role identity from mixed accompaniment
seonghobae Aug 16, 2026
92ecd30
test(roles): keep mixed-stem evidence off named accompaniment roles
seonghobae Aug 16, 2026
0940d71
fix(roles): keep mixed accompaniment evidence role-agnostic
seonghobae Aug 16, 2026
8cf7d26
docs(changelog): record role-identity-safe overlap warnings
seonghobae Aug 16, 2026
9f1e166
fix(roles): fail closed on missing windows and lock identity tests
cursoragent Aug 16, 2026
23678f3
fix(roles): stop naming keyboard parts from mixed accompaniment
cursoragent Aug 16, 2026
7f45a29
test(roles): lock measured overlap priority refresh
cursoragent Aug 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ Last updated: 2026-03-11
- likely harmony by section and by role
- section roadmap with entries, dropouts, pickups, stops, tags, and handoffs
- groove and timing cues relevant to locking the band together
- playable ranges and density or overlap warnings
- playable ranges and density or overlap warnings measured per section from
pitched-stem spectra (`song -> section -> role`), never fabricated
song-wide clash copy when stems or matching section windows are absent
- simplification, transposition, capo, tuning, or setup cues where applicable
- role-specific rehearsal priorities and confidence flags
- cue-sheet or chart-style exports that summarize the analysis in rehearsal-friendly form
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
- Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace.
- 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함.

### Changed

- Reduce register-overlap pair work by comparing only stems that meet the measured occupancy threshold for each register band while preserving deterministic result ordering.

### Fixed

- Keep silent stems from becoming zero-severity rehearsal overlap warnings at a zero threshold, and fail closed on invalid negative, non-finite, or boolean threshold configuration.
- Derive section-level register-overlap warnings from measured stem spectra instead of fabricating keyboard or vocal clash copy when no audio evidence exists.
- Keep mixed `other`-stem accompaniment evidence role-agnostic instead of assigning the same overlap warning and rehearsal priority to specific keyboard-hand or guitar roles the separator cannot identify.
- Omit register-overlap warnings when section windows are missing or do not match the section list, instead of measuring the whole song and repeating that average on every card.
- Keep mixed `other` stem energy from marking Keyboard Left Hand, Keyboard Right Hand, or Acoustic Guitar as active parts.
- Replace fabricated demo and browser-fallback clash copy with the same next-action register-overlap wording the engine emits for unambiguous bass and vocal roles.

## [0.1.3] - 2026-04-29

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ mod tests {
"setupNote": "Keep the attack short so the verse breathes.",
"manualOverrides": [],
"overlapWarnings": [
"Density warning: competing with Keyboard Left Hand in low register."
"The low register is crowded between Bass Guitar and accompaniment. Thin one part in this section so players can hear their cue."
]
}
],
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function succeededResult() {
setupNote: "Keep the attack short so the verse breathes.",
manualOverrides: [],
overlapWarnings: [
"Density warning: competing with Keyboard Left Hand in low register."
"The low register is crowded between Bass Guitar and accompaniment. Thin one part in this section so players can hear their cue."
]
},
{
Expand Down
14 changes: 14 additions & 0 deletions apps/desktop/src/features/workspace/SectionRoadmap.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ describe("SectionRoadmap", () => {
expect(onSongUpdate).toHaveBeenCalledTimes(1);
});

it("shows measured overlap copy from the demo song instead of a named keyboard clash", () => {
setNavigatorLanguage("en-US");
const song = createDemoRehearsalSong();

render(<SectionRoadmap song={song} activeRole={null} />);

expect(
screen.getByText(
"The low register is crowded between Bass Guitar and accompaniment. Thin one part in this section so players can hear their cue."
)
).toBeTruthy();
expect(screen.queryByText(/Density warning|Melodic overlap|Keyboard Left Hand/)).toBeNull();
});

it("does not update when the trimmed chord is unchanged", () => {
setNavigatorLanguage("en-US");
const song = createDemoRehearsalSong();
Expand Down
83 changes: 83 additions & 0 deletions docs/doctoring/register-overlap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Register-overlap doctoring

BandScope density warnings are a rehearsal cue, not a studio mix verdict.
They answer: *in this section, which pitched parts share a register so a
player should thin, simplify, or listen for a cue before the room starts.*

## Analysis target

Warnings follow the `song -> section -> role` hierarchy in
`ARCHITECTURE.md`. A song-wide FFT over mixed stems would hide the verse
that is muddy and the chorus that is already clear. Section windows reuse
the same boundary list that drives stem activity, so overlap is a
time-local observation rather than an atomistic song average.

Four-stem separation (`vocals`, `bass`, `drums`, `other`) cannot honestly
name Keyboard Left Hand versus Acoustic Guitar. The mixed `other` stem is
labeled accompaniment. Inventing a keyboard clash from that stem is a
product lie. The same honesty applies to presence: `map_stems_to_roles`
must not mark Keyboard Left Hand, Keyboard Right Hand, or Acoustic Guitar
active just because mixed `other` has energy. Browser-fallback and shared
demo fixtures use the same next-action copy the engine emits, and they
never attach that copy to a named keyboard or guitar role.

## Psychoacoustic and MIR basis

Auditory scene analysis treats concurrent sources as streams that compete
when they occupy the same spectral region (Bregman, 1990). Simultaneous
masking and critical-band overlap explain why two pitched parts in one
register become hard to hear and hard to lock (Moore, 2012; Fastl &
Zwicker, 2007). Equal-loudness contours (ISO, 2023) are not used as a
loudness meter here; they justify treating low, mid, and high registers as
perceptually different work for a band rather than as interchangeable FFT
bins.

Music-information-retrieval practice extracts spectral energy
distributions as timbre and texture descriptors (Tzanetakis & Cook, 2002;
Peeters, 2004). BandScope uses a three-band magnitude-squared real FFT
share, then reports a pair only when both pitched stems occupy the same
band above a finite threshold. Drums stay unpitched because broadband
transients do not mark a rehearsal register.

Temporal structure uses the same section boundaries as novelty-based form
analysis already present in the engine (Foote, 2000; Paulus et al., 2010).
That keeps overlap aligned with the roadmap a player actually rehearses.
If stems arrive without a matching boundary for every section, the
extractor emits no overlap warning rather than averaging the whole song.

## What the player should do next

Copy is action-first: name the crowded register, name the two sides, and
tell the player to thin one part in *this* section. It does not declare a
correct voicing.

## References

Bregman, A. S. (1990). *Auditory scene analysis: The perceptual
organization of sound*. The MIT Press.

Fastl, H., & Zwicker, E. (2007). *Psychoacoustics: Facts and models*
(3rd ed.). Springer. https://doi.org/10.1007/978-3-540-68888-4

Foote, J. (2000). Automatic audio segmentation using a measure of audio
novelty. In *Proceedings of the IEEE International Conference on
Multimedia and Expo* (Vol. 1, pp. 452–455). IEEE.
https://doi.org/10.1109/ICME.2000.869637

International Organization for Standardization. (2023). *Acoustics —
Normal equal-loudness-level contours* (ISO 226:2023).

Moore, B. C. J. (2012). *An introduction to the psychology of hearing*
(6th ed.). Brill.

Paulus, J., Müller, M., & Klapuri, A. (2010). Audio-based music structure
analysis. In *Proceedings of the 11th International Society for Music
Information Retrieval Conference* (pp. 625–630). ISMIR.

Peeters, G. (2004). *A large set of audio features for sound description
(similarity and classification) in the CUIDADO project* (Technical
report). IRCAM.

Tzanetakis, G., & Cook, P. (2002). Musical genre classification of audio
signals. *IEEE Transactions on Speech and Audio Processing, 10*(5),
293–302. https://doi.org/10.1109/TSA.2002.800560
59 changes: 59 additions & 0 deletions docs/plans/2026-08-16-register-overlap-demo-honesty.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Register-overlap demo and presence honesty

**Goal:** Stop the buyer-visible lie that mixed htdemucs `other` is
Keyboard Left Hand, Keyboard Right Hand, or Acoustic Guitar. Demo
fixtures and stem-activity mapping must use the same fail-closed
identity contract as measured overlap warnings.

**Architecture:** `map_stems_to_roles` maps only `vocals` and `bass` to
named roles. Shared-types, browser-fallback, and Rust contract fixtures
reuse engine next-action copy on unambiguous roles only.

**Tech Stack:** Python 3.12 activity mapper, TypeScript shared contracts,
React SectionRoadmap, Tauri serde fixtures.

## Task

1. Keep `other` from activating named keyboard or guitar roles.
2. Replace fabricated `Density warning` / `Melodic overlap` demo copy
with measured-style next-action wording.
3. Leave keyboard-hand and guitar demo warnings empty.
4. Keep heuristic no-stem extraction unchanged.

## Security Notes

### Attack surface

- In-memory stem-activity booleans already admitted by orchestration
- Demo rehearsal-song strings rendered in the desktop WebView

### Trust boundary

- Python activity mapper -> shared rehearsal-role contract -> React
workspace cards and Tauri serde fixtures

### Mitigations

- No file I/O, network, or subprocess in activity mapping
- Mixed `other` cannot authorize a named accompaniment role
- Demo copy is allowlisted next-action text, not a user-controlled path

### Test points

- `other`-only activity leaves keys and guitar inactive
- Extractor with bass+other keeps those roles out of `active_roles`
- Shared demo song rejects fabricated identity strings
- SectionRoadmap renders the honest bass next-action sentence

### Realistic threats

- Warning text injection is not a new channel: demo strings are
repository fixtures, and engine copy stays allowlisted
- Presence under-claiming (accompaniment plays but no named card) is
preferred to over-claiming three false parts

### Remaining risk

- Four-stem separation still cannot offer a dedicated accompaniment
role card. Add that role only with a later contract, not by renaming
`other` into keys or guitar.
68 changes: 68 additions & 0 deletions docs/plans/2026-08-16-register-overlap-role-wiring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Register-overlap role wiring

**Goal:** Stop fabricating keyboard and vocal clash copy. Attach
FFT-derived register-overlap warnings to rehearsal roles per section so a
player can thin a crowded register before rehearsal.

**Architecture:** `detect_register_overlap` stays a pure in-memory
feature. `RoleExtractor` slices admitted stems to each section window,
formats honest accompaniment labels, and copies roles so warnings and
priority stay section-local.

**Tech Stack:** Python 3.12, numpy real FFT, pytest, existing role
contracts.

## Task

1. Keep heuristic extraction (no stems) at empty `overlapWarnings`.
2. Measure overlap only when every section has a matching boundary;
missing or mismatched windows fail closed to no warnings.
3. Map `vocals` and `bass` to Lead Vocal and Bass Guitar. Keep mixed
`other` in player-facing copy as accompaniment, but do not assign that
evidence to Keyboard Left Hand, Keyboard Right Hand, or Acoustic Guitar.
4. Recalculate rehearsal priority from the section-local warnings.
5. Fail closed to no warnings when mapping throws.

## Security Notes

### Attack surface

- In-memory stem arrays and section boundary timestamps already admitted
by canonical orchestration
- Role-warning strings rendered in the desktop WebView

### Trust boundary

- Python analysis engine -> shared rehearsal-role contract -> React
workspace cards

### Mitigations

- No file I/O, network, or subprocess in overlap formatting
- Invalid windows, non-array stems, and mapping exceptions return empty
warnings
- Copy stays derived from measured shares; mixed `other` is not renamed
into a specific keyboard or guitar identity

### Test points

- Known 80 Hz bass+accompaniment verse versus 1 kHz chorus fixture
- Empty warnings when stems are absent or section windows are missing
- Mixed `other` overlap warns only the unambiguous stem-side role
- Mapping exception omits warnings without aborting extraction
- Invalid slice windows return empty arrays

### Realistic threats

- Oversized admitted audio already owned by `#781` / `#866`; this feature
must not add a second sample or stem ceiling
- Warning text injection is not a new channel: strings are engine-generated
from allowlisted stem and band names

### Remaining risk

- Four-stem `other` still cannot separate keys from guitar. Finer role
identity needs a later source-separation or user-override path, not
fabricated names. Presence mapping and demo fixtures now follow the
same rule: mixed accompaniment does not activate or warn a named
keyboard or guitar role.
8 changes: 3 additions & 5 deletions packages/shared-types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ const demoRehearsalSongSeed: RehearsalSong = {
transpositionPlan: "If the singer drops to B minor, keep the shape a whole step lower and let keys keep the color tones.",
manualOverrides: [],
overlapWarnings: [
"Density warning: competing with Keyboard Left Hand in low register."
"The low register is crowded between Bass Guitar and accompaniment. Thin one part in this section so players can hear their cue."
]
},
{
Expand Down Expand Up @@ -507,9 +507,7 @@ const demoRehearsalSongSeed: RehearsalSong = {
setupNote: "Keep the patch bright enough to stay over the guitars.",
transpositionPlan: "If the band rehearses in D, keep the voicing in first inversion so the top line still sings.",
manualOverrides: [],
overlapWarnings: [
"Melodic overlap: top notes conflict with Lead Vocal range."
]
overlapWarnings: []
},
{
id: "lead-vocal",
Expand Down Expand Up @@ -550,7 +548,7 @@ const demoRehearsalSongSeed: RehearsalSong = {
}
],
overlapWarnings: [
"Melodic overlap: competing with Keyboard 1 Right Hand."
"The mid register is crowded between accompaniment and Lead Vocal. Thin one part in this section so players can hear their cue."
]
}
],
Expand Down
11 changes: 11 additions & 0 deletions packages/shared-types/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,17 @@ describe("shared type helpers", () => {
});

expect(song.sections[0]?.roles[2]?.harmony?.source).toBe("model");
expect(song.sections[0]?.roles[0]?.overlapWarnings).toEqual([
"The low register is crowded between Bass Guitar and accompaniment. Thin one part in this section so players can hear their cue."
]);
expect(song.sections[0]?.roles[1]?.overlapWarnings).toEqual([]);
expect(song.sections[0]?.roles[2]?.overlapWarnings).toEqual([
"The mid register is crowded between accompaniment and Lead Vocal. Thin one part in this section so players can hear their cue."
]);
const fabricatedIdentity = /Density warning|Melodic overlap|Keyboard Left Hand|Keyboard 1 Right Hand/;
for (const role of song.sections[0]?.roles ?? []) {
expect(role.overlapWarnings.join(" ")).not.toMatch(fabricatedIdentity);
}
expect(song.sections[0]?.roles[0]?.harmonicExplanation).toContain("tonal floor");
expect(song.sections[0]?.roles[0]?.transpositionPlan).toContain("whole step lower");
expect(song.collaboration?.assignments).toHaveLength(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ def map_stems_to_roles(stem_activity: dict[str, bool]) -> dict[str, bool]:
- vocals -> lead-vocal
- bass -> bass-guitar
- drums -> (no dedicated role, contributes to groove detection)
- other -> keys-right, keys-left, acoustic-guitar
- other -> no named keyboard or guitar role. Mixed accompaniment
energy cannot establish Keyboard Left Hand, Keyboard Right Hand,
or Acoustic Guitar identity.

Args:
stem_activity: Dict mapping stem names to active booleans.
Expand All @@ -107,14 +109,13 @@ def map_stems_to_roles(stem_activity: dict[str, bool]) -> dict[str, bool]:
"""
vocals_active = stem_activity.get("vocals", False)
bass_active = stem_activity.get("bass", False)
other_active = stem_activity.get("other", False)

return {
"bass-guitar": bass_active,
"keys-left": other_active,
"keys-right": other_active,
"keys-left": False,
"keys-right": False,
"lead-vocal": vocals_active,
"acoustic-guitar": other_active,
"acoustic-guitar": False,
}


Expand Down
Loading
Loading