Source: public/modules/core/controllers/media-playback.client.controller.js

'use strict';

/**
 * In the demo application, the `MediaPlaybackController` is responsible for
 * initiating the playback of media excerpts.
 *
 * @class Angular.MediaPlaybackController
 */

angular.module('core').controller('MediaPlaybackController', [
    '$scope',
    'TrialData',
    '$timeout',
    'ExperimentManager',
    '$log',
    'OSC',
    function($scope, TrialData, $timeout, ExperimentManager, $log, OSC) {

        $log.debug('Loading MediaPlaybackController.');

        var thisController = this;


        /***** $scope *****/


        /**
         * The `MediaPlaybackController`'s `$scope` object. All properties on
         * `$scope` are available to the view.
         *
         * @name $scope
         * @namespace
         * @instance
         * @memberof Angular.MediaPlaybackController
         * @type {{}}
         */

        /**
         * The initial label for the button in the view.
         *
         * @name currentButtonLabel
         * @memberof Angular.MediaPlaybackController#$scope
         * @instance
         * @type {string}
         */
        $scope.currentButtonLabel = 'Begin Playback';

        /**
         * Indicates whether or not the media excerpt has been played.
         *
         * @name mediaHasPlayed
         * @memberof Angular.MediaPlaybackController#$scope
         * @instance
         * @type {boolean}
         */
        $scope.mediaHasPlayed = false;

        /**
         * Indicates whether or not the 'continue' button is disabled.
         *
         * @name buttonDisabled
         * @memberof Angular.MediaPlaybackController#$scope
         * @instance
         * @type {boolean}
         */
        $scope.buttonDisabled = false;

        /**
         * Sends a message to the Max helper application to initiate media
         * playback. The message includes the current session number and the
         * media label (according to the [study specification
         * document](index.html)), both retrieved from the {@link
         * Angular.TrialData|TrialData} service. This object sent with this
         * message is formatted like the following:
         *
         * ```
         * {
         *     oscType: 'message,
         *     address: '/eim/control/mediaID',
         *     args: [
         *         {
         *             type: 'string',
         *             value: <MEDIALABEL>
         *         },
         *         {
         *             type: 'string',
         *             value: <SESSIONNUMBER>
         *         }
         *     ]
         * }
         * ```
         *
         * When this method is called, {@link
         * Angular.MediaPlaybackController#$scope#buttonAction|$scope#buttonAction}
         * is bound to {@link Angular.ExperimentManager#advanceSlide|ExperimentManager#advanceSlide}.
         *
         * @function startMediaPlayback
         * @memberof Angular.MediaPlaybackController#$scope
         * @instance
         * @return {undefined}
         */
        $scope.startMediaPlayback = function() {

            var mediaIndex = TrialData.data.state.mediaPlayCount;
            var mediaLabel = TrialData.data.media[mediaIndex].label;

            $log.debug('Requesting playback of \'' + mediaLabel + '\'.');

            OSC.send({
                oscType: 'message',
                address: '/eim/control/mediaID',
                args: [
                    {
                        type: 'string',
                        value: '' + mediaLabel
                    },
                    {
                        type: 'string',
                        value: '' + TrialData.data.metadata.session_number
                    }
                ]
            });

            $log.debug('Binding $scope.buttonAction in' +
                ' MediaPlaybackController to ExperimentManager.advanceSlide');

            $scope.buttonAction = ExperimentManager.advanceSlide;
        };

        /**
         * The method to which the button in the view is bound. Initially,
         * the button is bound to {@link
         * Angular.MediaPlaybackController#$scope#startMediaPlayback|$scope#startMediaPlayback}.
         *
         * @name buttonAction
         * @memberof Angular.MediaPlaybackController#$scope
         * @instance
         * @type {function}
         */
        $scope.buttonAction = $scope.startMediaPlayback;


        /***** $scope Event Handlers *****/


        // Send a message to stop media playback when this controller is
        // destroyed. Also, remove OSC message event listeners.
        $scope.$on('$destroy', function stopMediaPlayback() {

            $log.debug('MediaPlaybackController scope destroyed. Stopping' +
                ' playback, removing OSC listener, and showing body.');

            OSC.send({
                oscType: 'message',
                address: '/eim/control/stopMedia',
                args: {
                    type: 'string',
                    value: '' + TrialData.data.metadata.session_number
                }
            });

            OSC.unsubscribe(
                '/eim/status/playback',
                thisController.playbackMessageListener
            );

            OSC.unsubscribe(
                '/eim/status/emotionIndex',
                thisController.emotionIndexMessageListener
            );

            $scope.showBody();
        });


        /***** Inner Members *****/


        /**
         * Requests the most recently recorded emotion index from the server
         * over OSC. The message used to retrieve this value is like the
         * following:
         *
         * ```
         * {
         *     oscType: 'message',
         *     address: '/eim/control/emotionIndex',
         *     args: [
         *         {
         *             type: 'string',
         *             value: <SESSIONNUMBER>
         *         }
         *     ]
         * }
         * ```
         *
         * @function requestEmotionIndex
         * @memberof Angular.MediaPlaybackController
         * @instance
         * @return {undefined}
         */
        this.requestEmotionIndex = function requestEmotionIndexFn() {

            $log.debug('Requesting emotion index.');

            OSC.send({
                oscType: 'message',
                address: '/eim/control/emotionIndex',
                args: [
                    {
                        type: 'string',
                        value: '' + TrialData.data.metadata.session_number
                    }
                ]
            });
        };

        /**
         * This method is called when the server fires the controller receives
         * a message with the address `/eim/status/playback`. When this
         * occurs, if the message indicates that playback has begun, the page
         * is blacked out. If the message indicates that playback has ended,
         * the blackout is removed, the emotion index is requested, {@link
         * Angular.MediaPlaybackController#$scope#currentButtonLabel|$scope#currentButtonLabel}
         *  is set to `'Continue'`, {@link
         *  Angular.MediaPlaybackController#$scope#mediaHasPlayed|$scope#mediaHasPlayed}
         *  is set to `true`, and {@link
         *  Angular.MediaPlaybackController#$scope#buttonDisabled|$scope#buttonDisabled}
         *  is set to `true`.
         *
         * @function playbackMessageListener
         * @memberof Angular.MediaPlaybackController
         * @instance
         * @param {{}} msg The OSC message
         * @return {undefined}
         */
        this.playbackMessageListener = function playbackMessageListenerFn(msg) {
            switch (parseInt(msg.args[0].value)) {

                // If it was a stop message
                case 0:

                    // Request emotion index from Max
                    thisController.requestEmotionIndex();

                    // Fade in screen
                    $scope.showBody();

                    // Update state
                    $timeout(function() {
                        $scope.$apply(function() {
                            $scope.currentButtonLabel = 'Continue';
                            $scope.mediaHasPlayed = true;
                            $scope.buttonDisabled = true;
                        });
                    });
                    break;

                // If it was a start message
                case 1:

                    // Fade out screen
                    $scope.hideBody();
                    break;
            }
        };

        /**
         * This method is called when the server fires the controller receives
         * a message with the address `/eim/status/emotionIndex`. When this
         * occurs, {@link Angular.TrialData|TrialData} is updated with the
         * received emotion index, {@link
         * Angular.MediaPlaybackController#$scope#buttonDisabled|$scope#buttonDisabled}
         *  is set to `false`, and the slide is advanced by calling
         *  {@link
         *  Angular.ExperimentManager#advanceSlide|ExperimentManager#advanceSlide}.
         *
         * @function emotionIndexMessageListener
         * @memberof Angular.MediaPlaybackController
         * @instance
         * @param {{}} msg The OSC message
         * @return {undefined}
         */
        this.emotionIndexMessageListener =
            function emotionIndexMessageListenerFn(msg) {

                var emotionIndex = parseInt(msg.args[0].value);

                // Increment media play count
                TrialData.data.state.mediaPlayCount++;

                // Set emotion index in TrialData
                TrialData.setValueForPathForCurrentMedia(
                    'data.answers.emotion_indices',
                    emotionIndex
                );

                // Update state
                $timeout(function() {
                    $scope.$apply(function() {
                        $scope.buttonDisabled = false;
                        ExperimentManager.advanceSlide();
                    });
                });
            };


        /***** Initialization Logic *****/


        // Attach OSC message received listeners
        OSC.subscribe('/eim/status/playback', this.playbackMessageListener);
        OSC.subscribe(
            '/eim/status/emotionIndex',
            this.emotionIndexMessageListener
        );
    }
]);