UNPKG

adaptorex

Version:

Connect all your live interactive storytelling devices and software

165 lines (138 loc) 3.9 kB
/** * * simple sound plugin that uses npm package play-sound. * * @requires play-sound * @requires plugin * * @module sound/sound * @copyright Lasse Marburg 2022 * @license MIT */ var player = require('play-sound')(opts = {}) const plugin = require('../plugin.js') const file = require('../../file.js') var schema = require('./schema.json') const { ChildProcess } = require('child_process') class Sound extends plugin.Plugin { constructor() { super(schema) } setup(config, game) { super.setup(config, game) this.addTemplate("play", (payload, action) => { const title = `sound ${action.name}` let footer = {text: ""} let subtitle const body = [] if(payload.loop) { if(payload.loop < 0) { footer["text"] = "Repeat infinite " } else if(payload.loop != 1) { footer["text"] = `Repeat ${payload.loop}x ` } } for(let track of payload.tracks) { body.push({text: `${track}`}) } if(payload.next) { footer["text"] += `then: ${payload.next}` footer["next"] = payload.next } if(footer.text != "") { body.push(footer) } return {title, subtitle, body} }) // @todo play a silent testsound to check if player is available this.connected = true return this.schema } update(settings) { } async play(data, session) { let playback = new Playback(data, session, this.game) await playback.start() return playback.cancel.bind(playback) } } class Playback { /** * @type {ChildProcess} * child process executing the current audio playback */ process constructor(properties, session, game) { Object.assign(this, properties) this.session = session this.game = game } async start() { this.donePlayingCallback = this.session.getCallback(this.donePlaying.bind(this)) if(this.loop < 0) { this.loop = 99999 } if(!this.loop) { this.loop = 1 } this.play_index = 0 await this.playNext(this.tracks[0]) } playNext(track) { return new Promise((resolve, reject) => { if(this.status == "canceled") { return } this.current_audiofile = this.game.getFilePath(track) if(!file.exists(this.current_audiofile)) { throw new adaptor.NotFoundError(`Could not find audiofile ${this.current_audiofile}`) } this.process = player.play(this.current_audiofile, async err => { if(this.status == "canceled") { return } this.donePlayingCallback(err) }) if(!(this.process instanceof ChildProcess)) { return reject(new Error(`Could not play audio ${this.current_audiofile}. Unable to spawn process with audioplayer: ${player.player}`)) } this.process.on("error", err => { return reject(new Error(`Could not play audio ${this.current_audiofile} with audioplayer: ${player.player}\n${err.message}`)) }) this.process.on("spawn", () => { this.session.log.info(`Play audio ${this.current_audiofile} with ${player.player}`) return resolve() }) }) } async donePlaying(err) { if(err) { throw new Error(err) } this.session.log("Done playing " + this.tracks[this.play_index]) this.play_index ++ if(this.play_index >= this.tracks.length) { this.play_index = 0 this.loop -- if(this.loop <= 0) { this.session.log.info("Played last track in playlist") if(this.next) { this.session.next(this.next) } return } } try { await this.playNext(this.tracks[this.play_index]) } catch (error) { throw error } } cancel() { this.status = "canceled" if(this.process) { this.process.kill() } } } module.exports.Plugin = Sound