<commit_before>pc.extend(pc.audio, function () {
    var Channel;
    
    if (pc.audio.hasAudioContext()) {
        /**
        * @name pc.audio.Channel
        * @class A channel is created when the pc.audio.AudioManager begins playback of a pc.audio.Sound. Usually created internally by 
        * pc.audio.AudioManager#playSound or pc.audio.AudioManager#playSound3d. Developers usually won't have to create Channels manually.
        * @param {pc.audio.AudioManager} manager The AudioManager instance
        * @param {pc.audio.Sound} sound The sound to playback
        * @param {Object} options
        * @param {Number} [options.volume] The playback volume, between 0 and 1.
        * @param {Boolean} [options.loop] Whether the sound should loop when it reaches the end or not.
        */
        Channel = function (manager, sound, options) {
            options = options || {};
            this.volume = options.volume || 1;
            this.loop = options.loop || false;
            this.sound = sound;
                
            this.paused = false;
            this.suspended = false;

            this.startedAt = null;
            this.pausedAt = null;

            this.manager = manager;

            this.source = null;
            var context = manager.context;
            this.gain = context.createGain();

            this.resetTimeout = null;
        };
        
        Channel.prototype = {
            /**
            * @function
            * @name pc.audio.Channel#play
            * @description Begin playback of sound
            */
            play: function () {
                if (this.source) {
                    throw new Error('Call stop() before calling play()');
                }

                this._createSource();

                this.startedAt = this.manager.context.currentTime;

                // Initialize volume and loop
                this.setVolume(this.volume);
                this.setLoop(this.loop);

                this.source.start(0);

                this.manager.on('volumechange', this.onManagerVolumeChange, this);
                this.manager.on('suspend', this.onManagerSuspend, this);
                this.manager.on('resume', this.onManagerResume, this);
            },

            /**
            * @function
            * @name pc.audio.Channel#pause
            * @description Pause playback of sound. Call unpause() to resume playback from the same position
            */
            pause: function () {
                if (this.source) {
                    if (this.resetTimeout !== null) {
                      clearTimeout(this.resetTimeout);
                    }
                    this.paused = true;
                    this.pausedAt = (this.manager.context.currentTime - this.startedAt) % this.source.buffer.duration;
                    this.source.stop(0);
                    this.source = null;
                }
            },
            
            /**
            * @function
            * @name pc.audio.Channel#unpause
            * @description Resume playback of the sound. Playback resumes at the point that the audio was paused
            */
            unpause: function () {
                if (this.source || !this.paused) {
                    throw new Error('Call pause() before unpausing.');
                }

                this._createSource();

                // Initialize volume and loop
                this.setVolume(this.volume);
                this.setLoop(this.loop);

                this.startedAt -= this.pausedAt;
                var remainingTime = this.source.buffer.duration - this.pausedAt;
                this.source.start(0, this.pausedAt, remainingTime);

                this.resetTimeout = setTimeout(function () {
                    // The loop property may have changed since the sound was restarted so check...
                    if (this.getLoop()) {
                        this.stop();
                        this.play();
                    }
                }.bind(this), remainingTime * 1000);

                this.paused = false;
            },

            /**
            * @function 
            * @name pc.audio.Channel#stop
            * @description Stop playback of sound. Calling play() again will restart playback from the beginning of the sound.
            */
            stop: function () {
                if (this.source) {
                    if (this.resetTimeout !== null) {
                      clearTimeout(this.resetTimeout);
                    }
                    this.source.stop(0);
                    this.source = null;
                }

                this.manager.off('volumechange', this.onManagerVolumeChange, this);
                this.manager.off('suspend', this.onManagerSuspend, this);
                this.manager.off('resume', this.onManagerResume, this);

            },
            
            /**
            * @function
            * @name pc.audio.Channel#setLoop
            * @description Enable/disable the loop property to make the sound restart from the beginning when it reaches the end.
            */
            setLoop: function (loop) {
                this.loop = loop;
                if (this.source) {
                    this.source.loop = loop;
                }
            },

            /**
            * @function 
            * @name pc.audio.Channel#setVolume
            * @description Set the volume of playback between 0 and 1.
            */
            setVolume: function (volume) {
                this.volume = volume;
                if (this.gain) {
                    this.gain.gain.value = volume * this.manager.getVolume();
                }
            },

            isPlaying: function () {
                return (!this.paused && (this.source.playbackState === this.source.PLAYING_STATE));
            },

            getDuration: function () {<commit_msg>[FIX] Sounds now pause correctly if paused more than once on a single playthrough. Refactored looping code to no longer require a timeout. Fixed default parameter setting.<commit_after>pc.extend(pc.audio, function () {
    var Channel;
    
    if (pc.audio.hasAudioContext()) {
        /**
        * @name pc.audio.Channel
        * @class A channel is created when the pc.audio.AudioManager begins playback of a pc.audio.Sound. Usually created internally by 
        * pc.audio.AudioManager#playSound or pc.audio.AudioManager#playSound3d. Developers usually won't have to create Channels manually.
        * @param {pc.audio.AudioManager} manager The AudioManager instance
        * @param {pc.audio.Sound} sound The sound to playback
        * @param {Object} options
        * @param {Number} [options.volume=1] The playback volume, between 0 and 1.
        * @param {Boolean} [options.loop=false] Whether the sound should loop when it reaches the end or not.
        */
        Channel = function (manager, sound, options) {
            options = options || {};
            this.volume = (typeof options.volume === 'undefined') ? 1 : options.volume;
            this.loop = (typeof options.loop === 'undefined') ? false : options.loop;
            this.sound = sound;
                
            this.paused = false;
            this.suspended = false;

            this.startTime = 0;
            this.startOffset = 0;

            this.manager = manager;

            this.source = null;
            var context = manager.context;
            this.gain = context.createGain();
        };
        
        Channel.prototype = {
            /**
            * @function
            * @name pc.audio.Channel#play
            * @description Begin playback of sound
            */
            play: function () {
                if (this.source) {
                    throw new Error('Call stop() before calling play()');
                }

                this._createSource();

                // Initialize volume and loop
                this.setVolume(this.volume);
                this.setLoop(this.loop);

                this.startTime = this.manager.context.currentTime;
                this.source.start(0, this.startOffset % this.source.buffer.duration);

                this.manager.on('volumechange', this.onManagerVolumeChange, this);
                this.manager.on('suspend', this.onManagerSuspend, this);
                this.manager.on('resume', this.onManagerResume, this);
            },

            /**
            * @function
            * @name pc.audio.Channel#pause
            * @description Pause playback of sound. Call unpause() to resume playback from the same position
            */
            pause: function () {
                if (this.source) {
                    this.paused = true;

                    this.startOffset += this.manager.context.currentTime - this.startTime;
                    this.source.stop(0);
                    this.source = null;
                }
            },

            /**
            * @function
            * @name pc.audio.Channel#unpause
            * @description Resume playback of the sound. Playback resumes at the point that the audio was paused
            */
            unpause: function () {
                if (this.source || !this.paused) {
                    throw new Error('Call pause() before unpausing.');
                }

                this._createSource();

                // Initialize volume and loop
                this.setVolume(this.volume);
                this.setLoop(this.loop);

                this.startTime = this.manager.context.currentTime;
                this.source.start(0, this.startOffset % this.source.buffer.duration);

                this.paused = false;
            },

            /**
            * @function 
            * @name pc.audio.Channel#stop
            * @description Stop playback of sound. Calling play() again will restart playback from the beginning of the sound.
            */
            stop: function () {
                if (this.source) {
                    this.source.stop(0);
                    this.source = null;
                }

                this.manager.off('volumechange', this.onManagerVolumeChange, this);
                this.manager.off('suspend', this.onManagerSuspend, this);
                this.manager.off('resume', this.onManagerResume, this);
            },
            
            /**
            * @function
            * @name pc.audio.Channel#setLoop
            * @description Enable/disable the loop property to make the sound restart from the beginning when it reaches the end.
            */
            setLoop: function (loop) {
                this.loop = loop;
                if (this.source) {
                    this.source.loop = loop;
                }
            },

            /**
            * @function 
            * @name pc.audio.Channel#setVolume
            * @description Set the volume of playback between 0 and 1.
            */
            setVolume: function (volume) {
                this.volume = volume;
                if (this.gain) {
                    this.gain.gain.value = volume * this.manager.getVolume();
                }
            },

            isPlaying: function () {
                return (!this.paused && (this.source.playbackState === this.source.PLAYING_STATE));
            },

            getDuration: function () {
{'fraction_non_alphanumeric': 0.09921716949599789, 'fraction_numerical': 0.0023748790570850557, 'mean_word_length': 1.3589211618257262, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3596564', 'n_tokens_mistral': 2856, 'n_tokens_neox': 2656, 'n_words': 707}