diff --git a/src/announcement-data/AnnouncementSystem.ts b/src/announcement-data/AnnouncementSystem.ts index 53cd5a860..ce22c2dc6 100644 --- a/src/announcement-data/AnnouncementSystem.ts +++ b/src/announcement-data/AnnouncementSystem.ts @@ -93,8 +93,16 @@ export type CustomAnnouncementButton = { export interface PluraliseOptions { andId: string - prefix?: string - finalPrefix?: string + prefix: string + finalPrefix: string + firstItemDelay: number + beforeItemDelay: number + beforeAndDelay: number + afterAndDelay: number +} + +const DefaultPluraliseOptions = { + andId: 'and', } export default abstract class AnnouncementSystem { @@ -238,8 +246,10 @@ export default abstract class AnnouncementSystem { * @param items Array of audio files * @returns Pluralised array of audio files */ - protected pluraliseAudio(items: AudioItem[], delay: number = 0, options: PluraliseOptions = { andId: 'and' }): AudioItem[] { - items = items + protected pluraliseAudio(items: AudioItem[], options: Partial = DefaultPluraliseOptions): AudioItem[] { + const _options = { ...DefaultPluraliseOptions, ...options } + + const _items = items .map(item => { if (typeof item === 'string') { return { id: item } @@ -249,27 +259,43 @@ export default abstract class AnnouncementSystem { }) .map((item, i) => { if (items.length - 1 === i) { - if (options.finalPrefix) { - item.id = `${options.finalPrefix}${item.id}` + if (_options.finalPrefix !== undefined) { + item.id = `${_options.finalPrefix}${item.id}` return item } } else { - if (options.prefix) { - item.id = `${options.prefix}${item.id}` + if (_options.prefix !== undefined) { + item.id = `${_options.prefix}${item.id}` return item } } + if (i === 0 && _options.firstItemDelay !== undefined) { + item.opts = { + ...item.opts, + delayStart: _options.firstItemDelay, + } + } else if (_options.beforeItemDelay !== undefined) { + item.opts = { + ...item.opts, + delayStart: _options.beforeItemDelay, + } + } + return item }) - if (items.length > 1) { - items.splice(items.length - 1, 0, { id: options.andId, opts: { delayStart: delay } }) - return items + if (_items.length > 1) { + _items.splice(_items.length - 1, 0, { id: _options.andId, opts: { delayStart: _options.beforeAndDelay } }) + + if (_options.afterAndDelay !== undefined || _options.beforeItemDelay !== undefined) { + _items[items.length - 1].opts ??= {} + _items[items.length - 1].opts!!.delayStart = _options.afterAndDelay ?? _options.beforeItemDelay + } } - return items + return _items } } diff --git a/src/announcement-data/systems/rolling-stock/BombardierXstar.ts b/src/announcement-data/systems/rolling-stock/BombardierXstar.ts index 3cf179e8f..216c08cdb 100644 --- a/src/announcement-data/systems/rolling-stock/BombardierXstar.ts +++ b/src/announcement-data/systems/rolling-stock/BombardierXstar.ts @@ -92,7 +92,7 @@ export default class BombardierXstar extends TrainAnnouncementSystem { if (remainingStops.length > 1) { // We are not at the termination point. files.push('calling at') - files.push(...this.pluraliseAudio(remainingStops, 75)) + files.push(...this.pluraliseAudio(remainingStops, { beforeAndDelay: 75 })) files.push('the next station is', remainingStops[0]) } diff --git a/src/announcement-data/systems/rolling-stock/TLClass700.tsx b/src/announcement-data/systems/rolling-stock/TLClass700.tsx index 882b00835..e26bc5329 100644 --- a/src/announcement-data/systems/rolling-stock/TLClass700.tsx +++ b/src/announcement-data/systems/rolling-stock/TLClass700.tsx @@ -157,7 +157,7 @@ export default class ThameslinkClass700 extends TrainAnnouncementSystem { files.push( ...this.pluraliseAudio( options.changeFor.map(poi => `station connections.${poi}`), - 50, + { beforeAndDelay: 50 }, ), ) } @@ -211,21 +211,11 @@ export default class ThameslinkClass700 extends TrainAnnouncementSystem { if (!this.validateStationExists(terminatesAtCode, 'low')) return files.push( - ...this.pluraliseAudio( - [ - ...callingAtCodes.map( - ({ crsCode }): AudioItemObject => ({ - id: `stations.high.${crsCode}`, - opts: { delayStart: 350 }, - }), - ), - { - id: `stations.low.${terminatesAtCode}`, - opts: { delayStart: 350 }, - }, - ], - 350, - ), + ...this.pluraliseAudio([...callingAtCodes.map(({ crsCode }) => `stations.high.${crsCode}`), `stations.low.${terminatesAtCode}`], { + beforeAndDelay: 350, + afterAndDelay: 350, + beforeItemDelay: 350, + }), ) } @@ -255,21 +245,11 @@ export default class ThameslinkClass700 extends TrainAnnouncementSystem { if (!this.validateStationExists(terminatesAtCode, 'low')) return files.push( - ...this.pluraliseAudio( - [ - ...callingAtCodes.map( - ({ crsCode }, i): AudioItemObject => ({ - id: `stations.high.${crsCode}`, - opts: { delayStart: 350 }, - }), - ), - { - id: `stations.low.${terminatesAtCode}`, - opts: { delayStart: 350 }, - }, - ], - 350, - ), + ...this.pluraliseAudio([...callingAtCodes.map(({ crsCode }) => `stations.high.${crsCode}`), `stations.low.${terminatesAtCode}`], { + beforeAndDelay: 350, + afterAndDelay: 350, + beforeItemDelay: 350, + }), ) } diff --git a/src/announcement-data/systems/rolling-stock/TfLElizabeth.tsx b/src/announcement-data/systems/rolling-stock/TfLElizabeth.tsx index d9cb176d6..9236b0bb5 100644 --- a/src/announcement-data/systems/rolling-stock/TfLElizabeth.tsx +++ b/src/announcement-data/systems/rolling-stock/TfLElizabeth.tsx @@ -1219,14 +1219,14 @@ export default class TfLElizabethLine extends AnnouncementSystem { if (nextStation.changeFor.length) { files.push( { id: 'conjoiners.change for', opts: { delayStart: 1000 } }, - ...this.pluraliseAudio(nextStation.changeFor, 0, { andId: 'conjoiners.and', prefix: 'change for.m.', finalPrefix: 'change for.e.' }), + ...this.pluraliseAudio(nextStation.changeFor, { andId: 'conjoiners.and', prefix: 'change for.m.', finalPrefix: 'change for.e.' }), ) } if (nextStation.exitFor.length) { files.push( { id: 'conjoiners.exit for', opts: { delayStart: 1000 } }, - ...this.pluraliseAudio(nextStation.exitFor, 0, { andId: 'conjoiners.and', prefix: 'exit for.m.', finalPrefix: 'exit for.e.' }), + ...this.pluraliseAudio(nextStation.exitFor, { andId: 'conjoiners.and', prefix: 'exit for.m.', finalPrefix: 'exit for.e.' }), ) } diff --git a/src/announcement-data/systems/rolling-stock/TfWTrainFx.tsx b/src/announcement-data/systems/rolling-stock/TfWTrainFx.tsx index 5dbd96491..4de42d6a5 100644 --- a/src/announcement-data/systems/rolling-stock/TfWTrainFx.tsx +++ b/src/announcement-data/systems/rolling-stock/TfWTrainFx.tsx @@ -45,7 +45,6 @@ export default class TfWTrainFx extends TrainAnnouncementSystem { files.push( ...this.pluraliseAudio( callingAtCodes.map(stn => stn.crsCode), - 0, { andId: 'conjoiners.and', prefix: 'stations.high.', finalPrefix: 'stations.low.' }, ), ) diff --git a/src/announcement-data/systems/stations/KeTechPhil.tsx b/src/announcement-data/systems/stations/KeTechPhil.tsx index e179a6ec8..ae7837984 100644 --- a/src/announcement-data/systems/stations/KeTechPhil.tsx +++ b/src/announcement-data/systems/stations/KeTechPhil.tsx @@ -39,6 +39,9 @@ export default class KeTechPhil extends StationAnnouncementSystem { readonly FILE_PREFIX = 'station/ketech/phil' readonly SYSTEM_TYPE = 'station' + private readonly CALLING_POINT_DELAY = 200 + private readonly CALLING_POINT_AND_DELAY = 100 + private get announcementPresets(): Readonly> { return { nextTrain: [ @@ -490,18 +493,14 @@ export default class KeTechPhil extends StationAnnouncementSystem { const allDestinations = [terminatingStation, dividesAt.splitCallingPoints!![dividesAt.splitCallingPoints!!.length - 1].crsCode] files.push( - ...this.pluraliseAudio( - allDestinations.map((stn, i) => ({ - id: stn, - opts: { delayStart: i === 0 ? 100 : 50 }, - })), - 100, - { - prefix: 'station.m.', - finalPrefix: 'station.e.', - andId: 'm.and', - }, - ), + ...this.pluraliseAudio(allDestinations, { + prefix: 'station.m.', + finalPrefix: 'station.e.', + andId: 'm.and', + firstItemDelay: 100, + beforeAndDelay: 100, + beforeItemDelay: 50, + }), ) } else { if (vias.length !== 0) { @@ -509,12 +508,10 @@ export default class KeTechPhil extends StationAnnouncementSystem { `station.m.${terminatingStation}`, 'm.via', ...this.pluraliseAudio( - vias.map((stn, i) => ({ - id: `station.${i === vias.length - 1 ? 'e' : 'm'}.${stn}`, - })), - 100, + vias.map((stn, i) => `station.${i === vias.length - 1 ? 'e' : 'm'}.${stn}`), { andId: 'm.and', + beforeAndDelay: 100, }, ), ) @@ -594,15 +591,14 @@ export default class KeTechPhil extends StationAnnouncementSystem { } else { files.push( 's.due to short platforms customers for', - ...this.pluraliseAudio( - plats.map(crs => ({ id: crs, opts: { delayStart: 100 } })), - 100, - { - prefix: 'station.m.', - finalPrefix: 'station.m.', - andId: 'm.and', - }, - ), + ...this.pluraliseAudio(plats, { + prefix: 'station.m.', + finalPrefix: 'station.m.', + andId: 'm.and', + beforeAndDelay: this.CALLING_POINT_AND_DELAY, + beforeItemDelay: this.CALLING_POINT_DELAY, + afterAndDelay: this.CALLING_POINT_AND_DELAY, + }), ...s.split(','), ) } @@ -612,15 +608,14 @@ export default class KeTechPhil extends StationAnnouncementSystem { id: 's.customers for', opts: { delayStart: 200 }, }, - ...this.pluraliseAudio( - plats.map(crs => ({ id: crs, opts: { delayStart: 100 } })), - 100, - { - prefix: 'station.m.', - finalPrefix: 'station.m.', - andId: 'm.and', - }, - ), + ...this.pluraliseAudio(plats, { + prefix: 'station.m.', + finalPrefix: 'station.m.', + andId: 'm.and', + beforeAndDelay: this.CALLING_POINT_AND_DELAY, + beforeItemDelay: this.CALLING_POINT_DELAY, + afterAndDelay: this.CALLING_POINT_AND_DELAY, + }), ...s.split(','), ) } @@ -645,11 +640,15 @@ export default class KeTechPhil extends StationAnnouncementSystem { if (reqStops.size === 0) return [] files.push( - ...this.pluraliseAudio( - Array.from(reqStops).map((s, i) => ({ id: s, opts: { delayStart: i === 0 ? 400 : 100 } })), - 100, - { prefix: 'station.m.', finalPrefix: 'station.m.', andId: 'm.and' }, - ), + ...this.pluraliseAudio(Array.from(reqStops), { + prefix: 'station.m.', + finalPrefix: 'station.m.', + andId: 'm.and', + firstItemDelay: 400, + beforeItemDelay: this.CALLING_POINT_DELAY, + beforeAndDelay: this.CALLING_POINT_AND_DELAY, + afterAndDelay: this.CALLING_POINT_AND_DELAY, + }), reqStops.size === 1 ? 'm.is a request stop and customers for this station' : 'm.are request stops and customers for these stations', 'm.should ask the conductor on the train to arrange for the train to stop', 'e.to allow them to alight', @@ -799,9 +798,13 @@ export default class KeTechPhil extends StationAnnouncementSystem { files.push( ...this.pluraliseAudio( - splitData.stopsUpToSplit.map(s => ({ id: `station.m.${s.crsCode}`, opts: { delayStart: 100 } })), - 100, - { andId: 'm.and' }, + splitData.stopsUpToSplit.map(s => `station.m.${s.crsCode}`), + { + andId: 'm.and', + beforeItemDelay: this.CALLING_POINT_DELAY, + beforeAndDelay: this.CALLING_POINT_AND_DELAY, + afterAndDelay: this.CALLING_POINT_AND_DELAY, + }, ), ) @@ -850,9 +853,13 @@ export default class KeTechPhil extends StationAnnouncementSystem { return [ { id: 's.customers for', opts: { delayStart: 400 } }, ...this.pluraliseAudio( - stops.map(s => ({ id: `station.m.${s}`, opts: { delayStart: 100 } })), - 100, - { andId: 'm.and' }, + stops.map(s => `station.m.${s}`), + { + andId: 'm.and', + beforeAndDelay: this.CALLING_POINT_AND_DELAY, + afterAndDelay: this.CALLING_POINT_AND_DELAY, + beforeItemDelay: this.CALLING_POINT_DELAY, + }, ), ] } @@ -922,19 +929,12 @@ export default class KeTechPhil extends StationAnnouncementSystem { files.push(`station.m.${terminatingStation}`, 'e.only') } else { files.push( - ...this.pluraliseAudio( - [ - ...callingPoints.map(stn => ({ - id: `station.m.${stn.crsCode}`, - opts: { delayStart: 100 }, - })), - `station.e.${terminatingStation}`, - ], - 100, - { - andId: 'm.and', - }, - ), + ...this.pluraliseAudio([...callingPoints.map(stn => `station.m.${stn.crsCode}`), `station.e.${terminatingStation}`], { + andId: 'm.and', + beforeItemDelay: this.CALLING_POINT_DELAY, + beforeAndDelay: this.CALLING_POINT_AND_DELAY, + afterAndDelay: this.CALLING_POINT_AND_DELAY, + }), ) } @@ -4068,6 +4068,29 @@ function LiveTrainAnnouncements({ nextTrainHandler, system }: LiveTrainAnnouncem const toc = processToc(firstUnannounced.operator, firstUnannounced.origin[0].crs, firstUnannounced.destination[0].crs) + const callingPoints = firstUnannounced.subsequentCallingPoints[0].callingPoint + + const callingAt = (callingPoints as any[]) + .map((p): any | null => { + if (p.isCancelled || p.et === 'Cancelled') return null + if (!system.stations.includes(p.crs)) return null + + return p + }) + .filter(x => !!x) + .map((p, i, arr): CallingAtPoint | null => { + console.log(`[${i} of ${arr.length - 1}]: ${p.crs}`) + + if (i === arr.length - 1 && p.crs === firstUnannounced.destination[0].crs) return null + + return { + crsCode: p.crs, + name: '', + randomId: '', + } + }) + .filter(x => !!x) as CallingAtPoint[] + const options: INextTrainAnnouncementOptions = { chime: 'four', hour: h === '00' ? '00 - midnight' : h, @@ -4078,21 +4101,7 @@ function LiveTrainAnnouncements({ nextTrainHandler, system }: LiveTrainAnnouncem platform: system.platforms.includes(firstUnannounced.platform.toLowerCase()) ? firstUnannounced.platform.toLowerCase() : '1', terminatingStationCode: firstUnannounced.destination[0].crs, vias: [], - callingAt: firstUnannounced.subsequentCallingPoints[0].callingPoint - .map((p, i): CallingAtPoint | null => { - if (p.isCancelled || p.et === 'Cancelled') return null - if (!system.stations.includes(p.crs)) return null - - if (i === firstUnannounced.subsequentCallingPoints[0].callingPoint.length - 1 && p.crs === firstUnannounced.destination[0].crs) - return null - - return { - crsCode: p.crs, - name: '', - randomId: '', - } - }) - .filter(x => !!x), + callingAt, } console.log(options) diff --git a/src/announcement-data/systems/stations/ScotRail.tsx b/src/announcement-data/systems/stations/ScotRail.tsx index c0b23bea1..b689fbad6 100644 --- a/src/announcement-data/systems/stations/ScotRail.tsx +++ b/src/announcement-data/systems/stations/ScotRail.tsx @@ -928,30 +928,6 @@ export default class ScotRail extends StationAnnouncementSystem { return files } - /** - * Takes an array of audio files, and adds an `and` audio file where needed. - * - * @example - * pluraliseAudioItems(['a', 'b', 'c']) // returns ['a', 'b', 'and', 'c'] - * - * @example - * pluraliseAudioItems(['a']) // returns ['a'] - * - * @example - * pluraliseAudioItems(['a', 'b']) // returns ['a', 'and', 'b'] - * - * @param items Array of audio files - * @returns Pluralised array of audio files - */ - protected pluraliseAudio(items: AudioItem[], delay: number = 0): AudioItem[] { - if (items.length > 1) { - items.splice(items.length - 1, 0, { id: 'conjoiner.and', opts: { delayStart: delay } }) - return items - } - - return items - } - private async playNextTrainAnnouncement(options: INextTrainAnnouncementOptions, download: boolean = false): Promise { const files: AudioItem[] = [] @@ -965,7 +941,9 @@ export default class ScotRail extends StationAnnouncementSystem { } else { const callingAtStops = options.callingAt.map(stn => stn.crsCode) files.push( - ...this.pluraliseAudio([...callingAtStops.map(stn => `stations.high._${stn}`), `stations.low._${options.terminatingStationCode}`]), + ...this.pluraliseAudio([...callingAtStops.map(stn => `stations.high._${stn}`), `stations.low._${options.terminatingStationCode}`], { + andId: 'conjoiner.and', + }), ) } @@ -1026,7 +1004,10 @@ export default class ScotRail extends StationAnnouncementSystem { files.push( { id: 'passengers for', opts: { delayStart: 400 } }, - ...this.pluraliseAudio(alternativeService.passengersFor.map(stop => `stations.high.${stop.crsCode}`)), + ...this.pluraliseAudio( + alternativeService.passengersFor.map(stop => `stations.high.${stop.crsCode}`), + { andId: 'conjoiner.and' }, + ), 'your next fastest direct service is now expected to be the', `times.hour.${hour}`, `times.mins.${minute}`,