Fix panning sounds after scrolling.

This commit is contained in:
Storm Dragon committed 2026-10-14 12:50:44 -04:00
1 parent b2f558c26c
commit 8e76d2341c
14 files changed
+338 -17

No files matched your search

+20
View File
@@ -320,6 +320,7 @@ func presentDocumentResult(logger *slog.Logger, speech speechEnqueuer, result re
"source_path", result.DocumentPosition.Object.ID().Path,
"virtual_caret_offset", result.DocumentPosition.Offset,
"caret_sync_error", result.CaretErr,
"sound_geometry_error", result.DocumentSoundErr,
"braille_line_available", result.DocumentBrailleLine != nil,
"braille_read_error", result.DocumentBrailleErr)
}
@@ -406,6 +407,25 @@ func presentDocumentResult(logger *slog.Logger, speech speechEnqueuer, result re
fallbackText = fallbackSpeech.Text
}
}
if len(cues) > 0 && logger.Enabled(context.Background(), slog.LevelDebug) {
// Record the captured geometry actually used for cue generation.
// Linked headings use the link's geometry independently of the heading.
attributes := []any{
"bus", result.DocumentPosition.Snapshot.BusName,
"source_path", result.DocumentPosition.Snapshot.Path,
"role", result.DocumentPosition.Snapshot.Role,
"spatialize_object_sounds", options.SpatializeObjectSounds,
"sound_geometry", result.DocumentPosition.Snapshot.SoundGeometry,
"cues", cues,
}
if link := result.DocumentPosition.LinkedHeading; link != nil {
attributes = append(attributes,
"linked_heading_bus", link.BusName,
"linked_heading_path", link.Path,
"linked_heading_sound_geometry", link.SoundGeometry)
}
logger.Debug("document navigation sound positioning", attributes...)
}
role = result.DocumentPosition.Snapshot.Role
if result.DocumentWrapped {
if result.DocumentBackwards {
@@ -11,6 +11,7 @@ import (
"time"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/presentation"
"git.stormux.org/storm/cthulhu/internal/review"
)
@@ -73,6 +74,9 @@ func TestNativeLinkedHeadings(t *testing.T) {
t.Fatal(ctx.Err())
}
}
t.Run("sound geometry after scrolling", func(t *testing.T) {
testLinkedHeadingSoundGeometry(t, ctx, adapter, focus)
})
for _, tc := range []struct {
level int
name, title string
@@ -142,6 +146,72 @@ func TestNativeLinkedHeadings(t *testing.T) {
}
}
func testLinkedHeadingSoundGeometry(t *testing.T, ctx context.Context, adapter *atspi.Adapter, focus atspi.ObjectID) {
t.Helper()
tree, err := adapter.CaptureDocument(ctx, focus)
if err != nil {
t.Fatal(err)
}
cursor, err := review.NewDocumentCursor(tree)
if err != nil {
t.Fatal(err)
}
if _, err := cursor.SyncFocus(ctx, focus); err != nil {
t.Fatal(err)
}
cold, err := cursor.Move(ctx, review.DocumentHeading, 6, false)
if err != nil || !cold.Moved || cold.Position.LinkedHeading == nil {
t.Fatalf("cold offscreen heading = %+v, %v", cold, err)
}
if cold.Position.LinkedHeading.SoundGeometry.ObjectKnown {
t.Fatal("fixture heading must start offscreen without captured sound geometry")
}
worker, err := review.NewWorker(adapter, 2*time.Second)
if err != nil {
t.Fatal(err)
}
worker.SyncFocus(focus)
if err := worker.Start(ctx); err != nil {
t.Fatal(err)
}
defer worker.Stop(context.Background())
for _, step := range []struct {
backwards bool
name string
}{
{false, "Offscreen first topic"},
{false, "Offscreen second topic"},
{true, "Offscreen first topic"},
} {
if err := worker.MoveDocument(review.DocumentHeading, 6, step.backwards); err != nil {
t.Fatal(err)
}
select {
case result := <-worker.Results():
if result.Err != nil || result.CaretErr != nil || result.DocumentSoundErr != nil ||
!result.Moved || result.DocumentPosition.Snapshot.Name != step.name {
t.Fatalf("scrolled heading result = %+v", result)
}
link := result.DocumentPosition.LinkedHeading
if link == nil || !link.SoundGeometry.ObjectKnown || !link.SoundGeometry.ReferenceKnown {
t.Fatalf("scrolled link lacks fresh geometry: %+v", link)
}
if !result.DocumentPosition.Snapshot.SoundGeometry.ObjectKnown {
t.Fatal("scrolled heading lacks fresh geometry")
}
cues := presentation.SoundCues(*link, presentation.Options{
EnableSound: true, PlaySoundForRole: true, SpatializeObjectSounds: true,
})
if len(cues) != 1 || cues[0].Pan >= -0.3 {
t.Fatalf("left-column link lost leftward sound: %+v", cues)
}
t.Logf("%s after scroll: bounds=%+v pan=%g", step.name, link.SoundGeometry.Object, cues[0].Pan)
case <-ctx.Done():
t.Fatal(ctx.Err())
}
}
}
func linkedHeadingFixtureFocus(tree *atspi.AccessibleTree) atspi.ObjectID {
root, err := tree.Root()
if err != nil {