PageSourceSearch

http://voronezhfm.ru/js/3las/fallback/3las.liveaudioplayer.js

js voronezhfm.ru collected 2026-10-08 01:00:37 UTC 7,570 bytes, 146 lines download raw bytes

1/*
2    Live audio player is part of 3LAS (Low Latency Live Audio Streaming)
3    https://github.com/JoJoBond/3LAS
4*/
5var LiveAudioPlayer = /** @class */ (function () {
6    function LiveAudioPlayer(audio, logger, maxVolume, startOffset, variableSpeed) {
7        if (maxVolume === void 0) { maxVolume = 1.0; }
8        if (startOffset === void 0) { startOffset = 0.33; }
9        if (variableSpeed === void 0) { variableSpeed = false; }
10        this.Audio = audio;
11        this.Logger = logger;
12        this.MaxVolume = maxVolume;
13        this.StartOffset = startOffset;
14        this.VariableSpeed = variableSpeed;
15        this.OffsetMin = this.StartOffset - LiveAudioPlayer.OffsetVariance;
16        this.OffsetMax = this.StartOffset + LiveAudioPlayer.OffsetVariance;
17        // Set speed to default
18        this.PlaybackSpeed = 1.0;
19        // Reset variable for scheduling times
20        this.NextScheduleTime = 0.0;
21        // Create gain node for volume control
22        this.Amplification = this.Audio.createGain();
23        // Set volume to max
24        this.Amplification.gain.value = 1.0;
25        // Connect gain node to context
26        this.Amplification.connect(this.Audio.destination);
27    }
28    Object.defineProperty(LiveAudioPlayer.prototype, "Volume", {
29        get: function () {
30            // Get volume from gain node
31            return this.Amplification.gain.value;
32        },
33        set: function (value) {
34            // Clamp value to [1e-20 ; MaxVolume]
35            if (value > 1.0)
36                value = this.MaxVolume;
37            else if (value <= 0.0)
38                value = 1e-20;
39            // Cancel any scheduled ramps
40            this.Amplification.gain.cancelScheduledValues(this.Audio.currentTime);
41            // Change volume following a ramp (more userfriendly)
42            this.Amplification.gain.exponentialRampToValueAtTime(value, this.Audio.currentTime + 0.5);
43        },
44        enumerable: false,
45        configurable: true
46    });
47    // Recieves an audiobuffer and schedules it for seamless playback
48    LiveAudioPlayer.prototype.PushBuffer = function (buffer) {
49        // Check if this is the first buffer we received
50        if (this.NextScheduleTime == 0.0) {
51            // Start playing [StartOffset] s from now
52            this.NextScheduleTime = this.Audio.currentTime + this.StartOffset;
53        }
54        var duration;
55        if (this.VariableSpeed)
56            duration = buffer.duration / this.PlaybackSpeed; // Use regular duration
57        else
58            duration = buffer.duration; // Use duration adjusted for playback speed
59        // Before creating a buffer and scheduling playback, check if playing this buffer makes sense at all
60        // If a buffer should have been started so far in the past that it would have finished playing by now, we are better of skipping it.
61        // But we still need to move the time forward to keep future timings right.
62        if (this.NextScheduleTime + duration > this.Audio.currentTime) {
63            var skipDurationTime = void 0;
64            // If the playback start time is in the past but the playback end time is in the future, we need to partially play the buffer.
65            if (this.Audio.currentTime >= this.NextScheduleTime) {
66                // Calculate the time we need to skip
67                skipDurationTime = this.Audio.currentTime - this.NextScheduleTime + 0.05;
68            }
69            else {
70                // No skipping needed
71                skipDurationTime = 0.0;
72            }
73            // Check if we'd skip the whole buffer anyway
74            if (skipDurationTime < duration) {
75                // Create new audio source for the buffer
76                var sourceNode_1 = this.Audio.createBufferSource();
77                // Make sure the node deletes itself after playback
78                sourceNode_1.onended = function (_ev) {
79                    sourceNode_1.disconnect();
80                };
81                // Prevent looping (the standard says that it should be off by default)
82                sourceNode_1.loop = false;
83                // Pass audio data to source
84                sourceNode_1.buffer = buffer;
85                //Connect the source to the gain node
86                sourceNode_1.connect(this.Amplification);
87                if (this.VariableSpeed) {
88                    var scheduleOffset = this.NextScheduleTime - this.Audio.currentTime;
89                    // Check if we are to far or too close to target schedule time
90                    if (this.NextScheduleTime - this.Audio.currentTime > this.OffsetMax) {
91                        if (this.PlaybackSpeed < 1.0 + LiveAudioPlayer.SpeedCorrectionFactor) {
92                            // We are too slow, speed up playback (somewhat noticeable)
93                            this.Logger.Log("Buffer size too large, speeding up playback.");
94                            this.PlaybackSpeed = 1.0 + LiveAudioPlayer.SpeedCorrectionFactor;
95                            duration = buffer.duration / this.PlaybackSpeed;
96                        }
97                    }
98                    else if (this.NextScheduleTime - this.Audio.currentTime < this.OffsetMin) {
99                        if (this.PlaybackSpeed > 1.0 - LiveAudioPlayer.SpeedCorrectionFactor) {
100                            // We are too fast, slow down playback (somewhat noticeable)
101                            this.Logger.Log("Buffer size too small, slowing down playback.");
102                            this.PlaybackSpeed = 1.0 - LiveAudioPlayer.SpeedCorrectionFactor;
103                            duration = buffer.duration / this.PlaybackSpeed;
104                        }
105                    }
106                    else {
107                        // Check if we are in time		
108                        if ((this.PlaybackSpeed > 1.0 && (this.NextScheduleTime - this.Audio.currentTime < this.StartOffset)) ||
109                            (this.PlaybackSpeed < 1.0 && (this.NextScheduleTime - this.Audio.currentTime > this.StartOffset))) {
110                            // We are within our min/max offset, set playpacks to default
111                            this.Logger.Log("Buffer size within limits, using normal playback speed.");
112                            this.PlaybackSpeed = 1.0;
113                            duration = buffer.duration;
114                        }
115                    }
116                    // Set playback speed
117                    sourceNode_1.playbackRate.value = this.PlaybackSpeed;
118                }
119                // Schedule playback
120                sourceNode_1.start(this.NextScheduleTime + skipDurationTime, skipDurationTime);
121            }
122            else {
123                this.Logger.Log("Skipped buffer because it became too old.");
124            }
125        }
126        else {
127            this.Logger.Log("Skipped buffer because it was too old.");
128        }
129        // Move time forward
130        this.NextScheduleTime += duration;
131    };
132    LiveAudioPlayer.prototype.Reset = function () {
133        this.NextScheduleTime = 0.0;
134    };
135    LiveAudioPlayer.prototype.CheckBeforeDecode = function (playbackLength) {
136        if (this.NextScheduleTime == 0)
137            return true;
138        return this.NextScheduleTime + playbackLength > this.Audio.currentTime;
139    };
140    // Crystal oscillator have a variance of about +/- 20ppm
141    // So worst case would be a difference of 40ppm between two oscillators.
142    LiveAudioPlayer.SpeedCorrectionFactor = 40 / 1.0e6;
143    // Hystersis value for speed up/down trigger
144    LiveAudioPlayer.OffsetVariance = 0.2;
145    return LiveAudioPlayer;
146}());

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.