Skip to content

Commit 3965b04

Browse files
committed
Agenda enhancements
Add PointsOfInterest as non-source points, for providing accessible points when using assistive devices. Adds auto-generation of tracks based on a pattern.
1 parent 1e96d2d commit 3965b04

File tree

1 file changed

+84
-33
lines changed

1 file changed

+84
-33
lines changed

agenda/agenda.go

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import (
55
"encoding/hex"
66
"fmt"
77
"os"
8+
"slices"
89
"strings"
910
"time"
1011

1112
"github.com/gofrs/uuid"
12-
"gopkg.in/yaml.v3"
13+
yaml "gopkg.in/yaml.v3"
1314
)
1415

1516
var fileFormats = []string{"mp3", "m4a", "webm"}
@@ -22,8 +23,8 @@ func New(filename string) (*Agenda, error) {
2223
}
2324

2425
a := new(Agenda)
25-
err = yaml.Unmarshal(data, a)
26-
if err != nil {
26+
27+
if err := yaml.Unmarshal(data, a); err != nil {
2728
return nil, fmt.Errorf("failed to read YAML: %w", err)
2829
}
2930

@@ -104,12 +105,7 @@ func (a *Agenda) AllTracks() (out []*Track) {
104105
// Track already-seen audio files so that we do not list them twice
105106
var seen []string
106107
unseen := func(id string) bool {
107-
for _, i := range seen {
108-
if i == id {
109-
return false
110-
}
111-
}
112-
return true
108+
return !slices.Contains(seen, id)
113109
}
114110

115111
// Load Announcements first
@@ -203,6 +199,9 @@ type Room struct {
203199
// played.
204200
Sources []*Source `json:"sources" yaml:"sources"`
205201

202+
// PointsOfInterest are non-source points in the room which are made available as targets to screen accessibility tooling.
203+
PointsOfInterest []*PointOfInterest `json:"pointsOfInterest" yaml:"pointsOfInterest"`
204+
206205
// RoomTracks is a list of audio tracks to be played in a room, sourced from
207206
// everywhere. This is generally exclusive with Sources.
208207
RoomTracks []*Track `json:"roomTracks" yaml:"roomTracks"`
@@ -223,6 +222,12 @@ func (r *Room) generateIDs(a *Agenda) error {
223222
return err
224223
}
225224

225+
for _, p := range r.PointsOfInterest {
226+
if err := p.generateID(); err != nil {
227+
return fmt.Errorf("failed to generate ID for point of interest: %w", err)
228+
}
229+
}
230+
226231
for _, s := range r.Sources {
227232
err = s.generateIDs(a)
228233
if err != nil {
@@ -281,12 +286,7 @@ func (r *Room) AllTracks() (out []*Track) {
281286
// Track already-seen audio files so that we do not list them twice
282287
var seen []string
283288
unseen := func(id string) bool {
284-
for _, i := range seen {
285-
if i == id {
286-
return false
287-
}
288-
}
289-
return true
289+
return !slices.Contains(seen, id)
290290
}
291291

292292
// Iterate Sources first
@@ -313,13 +313,13 @@ func (r *Room) AllTracks() (out []*Track) {
313313
// Dimensions describe the dimensions of a space.
314314
type Dimensions struct {
315315
// Width is the left-to-right dimension.
316-
Width float64 `json:"width" yaml:"width"`
316+
Width float64 `json:"width" yaml:"width"`
317317

318318
// Height is the up-to-down dimension (the height of a room).
319319
Height float64 `json:"height" yaml:"height"`
320320

321321
// Depth is the forward-to-backward dimension.
322-
Depth float64 `json:"depth" yaml:"depth"`
322+
Depth float64 `json:"depth" yaml:"depth"`
323323
}
324324

325325
// Surfaces describe the surface material of a room.
@@ -334,9 +334,8 @@ type Surfaces struct {
334334
Up string `json:"up" yaml:"up"`
335335
}
336336

337-
// Source describes a unique audio sequence and location
338-
type Source struct {
339-
337+
// PointOfInterest describes a point of interest.
338+
type PointOfInterest struct {
340339
// ID is the generated unique identifier
341340
ID string `json:"id" yaml:"-"`
342341

@@ -346,34 +345,72 @@ type Source struct {
346345
// Location indicates a specific 3-dimensional coordinate in the room from
347346
// which the audio of this source emanates
348347
Location Point `json:"location" yaml:"location"`
348+
}
349+
350+
func (p *PointOfInterest) generateID() error {
351+
// If we don't have a name, generate one
352+
if p.Name == "" {
353+
p.Name = uuid.Must(uuid.NewV1()).String()
354+
}
355+
356+
p.ID = hashString(fmt.Sprintf("poi-%s", p.Name))
357+
358+
return nil
359+
}
360+
361+
// Source describes a unique audio sequence and location
362+
type Source struct {
363+
PointOfInterest `json:",inline" yaml:",inline"`
364+
365+
// AutoTracks defines a pattern by which cue-track mappings are generated.
366+
// NOTE: either RoomTracks or AutoTracks should be defined, but not both.
367+
AutoTracks *AutoTracks `json:"autoTracks" yaml:"autoTracks"`
349368

350369
// Tracks is the list of audio tracks which should be played upon reaching a
351370
// particular cue
352371
Tracks []*Track `json:"tracks" yaml:"tracks"`
353372
}
354373

355374
func (s *Source) generateIDs(a *Agenda) error {
356-
err := s.generateID()
357-
if err != nil {
375+
if err := s.generateID(); err != nil {
358376
return err
359377
}
360378

361-
for _, t := range s.Tracks {
362-
if err = t.generateID(a); err != nil {
363-
return err
379+
if s.AutoTracks != nil {
380+
var autoTracks []*Track
381+
382+
for _, c := range a.Cues {
383+
if slices.Contains(s.AutoTracks.IgnoreCues, c.Name) {
384+
continue
385+
}
386+
387+
t := &Track{
388+
AudioFilePrefix: s.AutoTracks.Prefix + c.Name,
389+
Cue: c.Name,
390+
}
391+
392+
if err := t.generateID(a); err != nil {
393+
if s.AutoTracks.IgnoreMissing {
394+
continue
395+
}
396+
397+
return fmt.Errorf("failed to generate track %s for cue %s in source %s: %w",
398+
t.AudioFilePrefix, t.Cue, s.Name, err)
399+
}
400+
401+
autoTracks = append(autoTracks, t)
364402
}
365-
}
366403

367-
return nil
368-
}
404+
s.Tracks = autoTracks
369405

370-
func (s *Source) generateID() error {
371-
// If we don't have a name, generate one
372-
if s.Name == "" {
373-
s.Name = uuid.Must(uuid.NewV1()).String()
406+
return nil
374407
}
375408

376-
s.ID = hashString(fmt.Sprintf("source-%s", s.Name))
409+
for _, t := range s.Tracks {
410+
if err := t.generateID(a); err != nil {
411+
return err
412+
}
413+
}
377414

378415
return nil
379416
}
@@ -460,6 +497,20 @@ func (t *Track) generateID(a *Agenda) error {
460497
return nil
461498
}
462499

500+
// AutoTracks defines a method by which cue-track mappings may be autogenerated.
501+
type AutoTracks struct {
502+
// Prefix defines a prefix which should be added to each auto-generated audio filename.
503+
// Note that this does NOT presume a directory separation between the prefix and the filename.
504+
// That is, a prefix of "alpha/beta" and a cue name of "gamma" would result in a file name "alpha/betagamma.webm".
505+
Prefix string `json:"prefix" yaml:"prefix"`
506+
507+
// IgnoreMissing indicates that any tracks which do not exist should simply be ignored.
508+
IgnoreMissing bool `json:"ignoreMissing" yaml:"ignoreMissing"`
509+
510+
// IgnoreCues provides a list of cues for which no track should be bound.
511+
IgnoreCues []string
512+
}
513+
463514
// Point is a 3-dimensional point in space
464515
type Point struct {
465516
X float64 `json:"x" yaml:"x"`

0 commit comments

Comments
 (0)