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

// Copyright (c) 2015-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.
//

/** Accessor to play an audio signal.
 *  This accessor accepts as input in a variety of formats and plays
 *  back the audio encoded in those input values.
 *
 *  This accessor queues the data to be played by the audio system.
 *  When the data has been accepted by the audio system to be played,
 *  it produces an output with value true. That output should be used
 *  as a trigger to provide more audio data. If that new output data
 *  is provided before the previously data has been drained by the audio
 *  queue, then continuous audio with no gaps is possible.
 *  If on the other hand input data is provided too quickly, then it
 *  will overwrite the data in the output buffer, thereby creating
 *  considerable distortion. If it is provided too slowly, then there
 *  will be gaps in the output audio.
 *
 *  The _inputFormat_ parameter specifies the form in which the audio
 *  input will be provided. The available formats include:
 *
 *  * "raw": The input is a byte array representing audio data exactly as
 *    captured by default on the host.
 *  * "array": The audio input data is an array of arrays of numbers,
 *    where each number is in the range from -1.0 to 1.0.
 *    The first index of the input specifies the channel number.
 *  * "encoded": The audio input data is a byte array containing audio
 *    data encoded in one of the file format standards such as
 *    AIFF (historically associated with Apple computers),
 *    AIFF-C (a compressed version of AIFF),
 *    AU (historically associated with Sun Microsystems and Unix computers), or
 *    WAVE (historically associated with Windows PCs).
 *
 *  The _playbackOptions_ parameter is an object with the following properties,
 *  all of which are optional:
 *
 *  * _bigEndian_: 1 if the the sample is big endian, 0 if it is little endian
 *    If _bigEndian_ is not present, then the default is big endian.
 *  * _bitsPerSample_: The number of bits per sample. This is an integer that
 *    defaults to 16.
 *  * _channels_: The number of channels. This defaults to 1.
 *  * _sampleRate_: The sample rate. This is an integer that defaults to 8000.
 *    Typical supported sample rates are 8000, 11025, 22050, 44100, and 48000.
 *
 *  WAVE (aka .wav) is 16 bits, 1 channel, little endian, 44100 Hz.

 *  This accessor requires the optional 'audio' module, which may or may
 *  not be provided by an accessor host. Moreover, a host may not support
 *  all capture formats and all output formats.
 *
 *  @input input The audio data.
 *  @output accepted An indicator that the audio data has been queued
 *   to the audio system.
 *  @parameter inputFormat The format of the input data.
 *  @parameter playbackOptions The playback options.
 *
 *  @accessor audio/AudioPlayer
 *  @author Edward A. Lee (eal@eecs.berkeley.edu)
 *  @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 exports, require*/
/*jshint globalstrict: true*/
"use strict";

// Set up the accessor.
exports.setup = function () {
    this.input('input');
    this.output('accepted', {
        type: 'boolean',
        spontaneous: true
    });
    this.parameter('inputFormat', {
        type: 'string',
        value: 'raw',
        options: ['raw', 'array', 'encoded']
    });
    this.parameter('playbackOptions', {
        value: {
            bitsPerSample: 16,
            channels: 1,
            sampleRate: 8000
        }
    });
};

var player = null;
var audio = require("audio");

exports.initialize = function () {
    var self = this;
    player = new audio.Player(
        self.getParameter('inputFormat'),
        self.getParameter('playbackOptions'));
    self.addInputHandler('input', function () {
        // FIXME: Input format.
        player.play(self.get('input'), function () {
            self.send('accepted', true);
        });
    });
};

exports.wrapup = function () {
    if (player !== null) {
        player.stop();
        player = null;
    }
};