@@ -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
1516var 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"`
@@ -281,12 +280,7 @@ func (r *Room) AllTracks() (out []*Track) {
281280 // Track already-seen audio files so that we do not list them twice
282281 var seen []string
283282 unseen := func (id string ) bool {
284- for _ , i := range seen {
285- if i == id {
286- return false
287- }
288- }
289- return true
283+ return ! slices .Contains (seen , id )
290284 }
291285
292286 // Iterate Sources first
@@ -313,13 +307,13 @@ func (r *Room) AllTracks() (out []*Track) {
313307// Dimensions describe the dimensions of a space.
314308type Dimensions struct {
315309 // Width is the left-to-right dimension.
316- Width float64 `json:"width" yaml:"width"`
310+ Width float64 `json:"width" yaml:"width"`
317311
318312 // Height is the up-to-down dimension (the height of a room).
319313 Height float64 `json:"height" yaml:"height"`
320314
321315 // Depth is the forward-to-backward dimension.
322- Depth float64 `json:"depth" yaml:"depth"`
316+ Depth float64 `json:"depth" yaml:"depth"`
323317}
324318
325319// Surfaces describe the surface material of a room.
@@ -334,9 +328,8 @@ type Surfaces struct {
334328 Up string `json:"up" yaml:"up"`
335329}
336330
337- // Source describes a unique audio sequence and location
338- type Source struct {
339-
331+ // PointOfInterest describes a point of interest.
332+ type PointOfInterest struct {
340333 // ID is the generated unique identifier
341334 ID string `json:"id" yaml:"-"`
342335
@@ -346,20 +339,58 @@ type Source struct {
346339 // Location indicates a specific 3-dimensional coordinate in the room from
347340 // which the audio of this source emanates
348341 Location Point `json:"location" yaml:"location"`
342+ }
343+
344+ // Source describes a unique audio sequence and location
345+ type Source struct {
346+ PointOfInterest `json:",inline" yaml:",inline"`
347+
348+ // AutoTracks defines a pattern by which cue-track mappings are generated.
349+ // NOTE: either RoomTracks or AutoTracks should be defined, but not both.
350+ AutoTracks * AutoTracks `json:"autoTracks" yaml:"autoTracks"`
349351
350352 // Tracks is the list of audio tracks which should be played upon reaching a
351353 // particular cue
352354 Tracks []* Track `json:"tracks" yaml:"tracks"`
353355}
354356
355357func (s * Source ) generateIDs (a * Agenda ) error {
356- err := s .generateID ()
357- if err != nil {
358+ if err := s .generateID (); err != nil {
358359 return err
359360 }
360361
362+ if s .AutoTracks != nil {
363+ var autoTracks []* Track
364+
365+ for _ , c := range a .Cues {
366+ if slices .Contains (s .AutoTracks .IgnoreCues , c .Name ) {
367+ continue
368+ }
369+
370+ t := & Track {
371+ AudioFilePrefix : s .AutoTracks .Prefix + c .Name ,
372+ Cue : c .Name ,
373+ }
374+
375+ if err := t .generateID (a ); err != nil {
376+ if s .AutoTracks .IgnoreMissing {
377+ continue
378+ }
379+
380+ return fmt .Errorf ("failed to generate track %s for cue %s in source %s: %w" ,
381+ t .AudioFilePrefix , t .Cue , s .Name , err )
382+ }
383+
384+ autoTracks = append (autoTracks , t )
385+ }
386+
387+ s .Tracks = autoTracks
388+
389+ return nil
390+ }
391+
361392 for _ , t := range s .Tracks {
362- if err = t .generateID (a ); err != nil {
393+ if err : = t .generateID (a ); err != nil {
363394 return err
364395 }
365396 }
@@ -460,6 +491,20 @@ func (t *Track) generateID(a *Agenda) error {
460491 return nil
461492}
462493
494+ // AutoTracks defines a method by which cue-track mappings may be autogenerated.
495+ type AutoTracks struct {
496+ // Prefix defines a prefix which should be added to each auto-generated audio filename.
497+ // Note that this does NOT presume a directory separation between the prefix and the filename.
498+ // That is, a prefix of "alpha/beta" and a cue name of "gamma" would result in a file name "alpha/betagamma.webm".
499+ Prefix string `json:"prefix" yaml:"prefix"`
500+
501+ // IgnoreMissing indicates that any tracks which do not exist should simply be ignored.
502+ IgnoreMissing bool `json:"ignoreMissing" yaml:"ignoreMissing"`
503+
504+ // IgnoreCues provides a list of cues for which no track should be bound.
505+ IgnoreCues []string
506+ }
507+
463508// Point is a 3-dimensional point in space
464509type Point struct {
465510 X float64 `json:"x" yaml:"x"`
0 commit comments