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.