Source: org/terraswarm/accessor/accessors/web/audio/ClipPlayer.js

// Copyright (c) 2016-2017 The Regents of the University of California.
// All rights reserved.
//
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.
//

/** An accessor for playing a sound clip from a URL or a resource on the
 *  local host. The URL or resource name can be provided as a parameter or
 *  as an input. To start playing the sound, an event must be provided to the
 *  start input. An event at the stop input, or a new event at the start input,
 *  will cause the sound to stop playing.
 *
 *  @accessor audio/ClipPlayer
 *  @author Elizabeth Osyk (beth@berkeley.edu)
 *  @input start A trigger input to start playback.
 *   The value is ignored and can be anything.
 *  @input stop A trigger to stop playback.
 *   The value is ignored and can be anything.
 *  @input clipURL The URL to retrieve the sound clip from.
 *  @version $$Id$$
 */

// Stop extra messages from jslint and jshint.  Note that there should
// be no space between the / and the * and global. See
// https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSHint */
/*globals console, error, exports, output, require, send */
/*jshint globalstrict: true*/
'use strict';

var audio = require("audio");

/** Create the inputs and outputs for this accessor. */
exports.setup = function () {
    this.input('start', {
    	'value' : true
    });
    this.input('stop');
    // Use default on icyphy so that
    // https://www.icyphy.org/accessors/library/index.html?accessor=audio.ClipPlayer
    // will work.
    this.input('clipURL', {
        'type': 'string',
        'value': 'https://www.icyphy.org/accessors/src/USAD2016_06-RhapsodyInBlueTrimmed.mp3'
    });
    this.output('done', {
        'type' : 'boolean'
    });
};

/** Load the specified URL and create a player for it.  */
exports.initialize = function () {
    var self = this;
    this.player = null;
    this.previousURL = null;

    /** Check the URL and, if changed, create a new player for it.  */
    function updateURL() {
        var url = self.get('clipURL');
        if (url && url !== self.previousURL) {
            //console.log('Got a new URL: ' + url);
            if (self.player !== null) {
                self.player.stop();        // audio.js checks if it's actually playing.
            }
    		
            self.player = new audio.ClipPlayer(url);
            self.previousURL = url;
            
            self.player.on('done', function() {
                self.send('done', true);
            });
        }
    }
    // Do not update in initialize. This causes an error
    // with the default URL if there is no network connection.
    // updateURL();

    this.addInputHandler('clipURL', function () {
        updateURL();
    });

    this.addInputHandler('start', function () {
        // In case there is a new URL...
        updateURL();

        if (self.player === null || typeof self.player === 'undefined') {
            error('No clip specified.');
            return;
        } else {
            self.player.stop();        // audio.js checks if it's actually playing.
        }
        self.player.play();
    });

    this.addInputHandler('stop', function () {
        if (self.player !== null && typeof self.player !== 'undefined') {
            self.player.stop();  // audio.js checks if it's actually playing.
        }
    });
    
};

/** Stop any playback. */
exports.wrapup = function () {
    if (this.player !== null && typeof this.player !== 'undefined') {
        this.player.stop();  // audio.js checks if it's actually playing.
        this.player = null;
    }
};