<commit_before>var Instrument = function(){
	var me = {};

	me.type = "sample";
	me.name = "";
	me.instrumentIndex = 0;
	me.sampleIndex = -1;
	me.fadeout = 128;
	me.data = [];
	me.samples = [Sample()];
	me.sample = me.samples[0];

	me.volumeEnvelope = {raw: [], enabled: false, points: [[0,48],[10,64],[20,40],[30,18],[40,28],[50,18]], count:6};
	me.panningEnvelope = {raw: [], enabled: false, points: [[0,32],[20,40],[40,24],[60,32],[80,32]], count:5};
	me.vibrato = {};

	me.sampleNumberForNotes = [];

	me.play = function(noteIndex,notePeriod,volume,track,trackEffects,time){
		if (Tracker.inFTMode()) {
			notePeriod = me.getPeriodForNote(noteIndex);
		}
		return Audio.playSample(me.instrumentIndex,notePeriod,volume,track,trackEffects,time,noteIndex);
	};

	me.noteOn = function(time){
		var volumeEnvelope;
		var panningEnvelope;
		var scheduled = {};

		if (me.volumeEnvelope.enabled){
			volumeEnvelope = Audio.context.createGain();
			var envelope = me.volumeEnvelope;
			var scheduledTime = processEnvelop(envelope,volumeEnvelope,time);
			if (scheduledTime) scheduled.volume = (time + scheduledTime);
		}

		if (me.panningEnvelope.enabled && Audio.usePanning){
			panningEnvelope = Audio.context.createStereoPanner();
			envelope = me.panningEnvelope;
			scheduledTime = processEnvelop(envelope,panningEnvelope,time);
			if (scheduledTime) scheduled.panning = (time + scheduledTime);
		}

		return {volume: volumeEnvelope, panning: panningEnvelope, scheduled: scheduled};
	};

	me.noteOff = function(time,noteInfo){
		if (!noteInfo || !noteInfo.volume) return;

		noteInfo.volume.gain.cancelScheduledValues(time);
		noteInfo.volumeFadeOut.gain.cancelScheduledValues(time);

		if (noteInfo.volumeEnvelope) noteInfo.volumeEnvelope.gain.cancelScheduledValues(time);
		if (noteInfo.panningEnvelope) noteInfo.panningEnvelope.pan.cancelScheduledValues(time);
        noteInfo.scheduled = undefined;

		if (Tracker.inFTMode()){
			var tickTime = Tracker.getProperties().tickTime;

			if (me.volumeEnvelope.enabled){

				if (me.volumeEnvelope.sustain && noteInfo.volumeEnvelope){
					var timeOffset = 0;
					var startPoint = me.volumeEnvelope.points[me.volumeEnvelope.sustainPoint];
					if (startPoint) timeOffset = startPoint[0]*tickTime;
					for (var p = me.volumeEnvelope.sustainPoint; p< me.volumeEnvelope.count;p++){
						var point = me.volumeEnvelope.points[p];
						noteInfo.volumeEnvelope.gain.linearRampToValueAtTime(point[1]/64,time + (point[0]*tickTime) - timeOffset);
					}
				}

				if (me.fadeout){
					var fadeOutTime = (65536/me.fadeout) * tickTime / 2;
					noteInfo.volumeFadeOut.gain.linearRampToValueAtTime(0,time + fadeOutTime);
				}

			}else{
				noteInfo.volumeFadeOut.gain.linearRampToValueAtTime(0,time + 0.1)
			}

            if (me.panningEnvelope.enabled && Audio.usePanning){
                timeOffset = 0;
                startPoint = me.panningEnvelope.points[me.panningEnvelope.sustainPoint];
                if (startPoint) timeOffset = startPoint[0]*tickTime;
                for (p = me.panningEnvelope.sustainPoint; p< me.panningEnvelope.count;p++){
                    point = me.panningEnvelope.points[p];
                    noteInfo.panningEnvelope.pan.linearRampToValueAtTime((point[1]-32)/32,time + (point[0]*tickTime) - timeOffset);
                }
            }

			return 100;

		}else{
			if (noteInfo.isKey && noteInfo.volume){
				noteInfo.volume.gain.linearRampToValueAtTime(0,time + 0.5)
			}else{
				return 0;
			}
		}

	};

	function processEnvelop(envelope,audioNode,time){
		var tickTime = Tracker.getProperties().tickTime;
		var maxPoint = envelope.sustain ? envelope.sustainPoint+1 : envelope.count;<commit_msg>re-check envelope points at NoteOff as instrument may heve been switched (example pos 47 in joy.xm)<commit_after>var Instrument = function(){
	var me = {};

	me.type = "sample";
	me.name = "";
	me.instrumentIndex = 0;
	me.sampleIndex = -1;
	me.fadeout = 128;
	me.data = [];
	me.samples = [Sample()];
	me.sample = me.samples[0];

	me.volumeEnvelope = {raw: [], enabled: false, points: [[0,48],[10,64],[20,40],[30,18],[40,28],[50,18]], count:6};
	me.panningEnvelope = {raw: [], enabled: false, points: [[0,32],[20,40],[40,24],[60,32],[80,32]], count:5};
	me.vibrato = {};

	me.sampleNumberForNotes = [];

	me.play = function(noteIndex,notePeriod,volume,track,trackEffects,time){
		if (Tracker.inFTMode()) {
			notePeriod = me.getPeriodForNote(noteIndex);
		}
		return Audio.playSample(me.instrumentIndex,notePeriod,volume,track,trackEffects,time,noteIndex);
	};

	me.noteOn = function(time){
		var volumeEnvelope;
		var panningEnvelope;
		var scheduled = {};

		if (me.volumeEnvelope.enabled){
			volumeEnvelope = Audio.context.createGain();
			var envelope = me.volumeEnvelope;
			var scheduledTime = processEnvelop(envelope,volumeEnvelope,time);
			if (scheduledTime) scheduled.volume = (time + scheduledTime);
		}

		if (me.panningEnvelope.enabled && Audio.usePanning){
			panningEnvelope = Audio.context.createStereoPanner();
			envelope = me.panningEnvelope;
			scheduledTime = processEnvelop(envelope,panningEnvelope,time);
			if (scheduledTime) scheduled.panning = (time + scheduledTime);
		}

		return {volume: volumeEnvelope, panning: panningEnvelope, scheduled: scheduled};
	};

	me.noteOff = function(time,noteInfo){
		if (!noteInfo || !noteInfo.volume) return;

		noteInfo.volume.gain.cancelScheduledValues(time);
		noteInfo.volumeFadeOut.gain.cancelScheduledValues(time);

		if (noteInfo.volumeEnvelope) noteInfo.volumeEnvelope.gain.cancelScheduledValues(time);
		if (noteInfo.panningEnvelope) noteInfo.panningEnvelope.pan.cancelScheduledValues(time);
        noteInfo.scheduled = undefined;

		if (Tracker.inFTMode()){
			var tickTime = Tracker.getProperties().tickTime;

			if (me.volumeEnvelope.enabled){

				if (me.volumeEnvelope.sustain && noteInfo.volumeEnvelope){
					var timeOffset = 0;
					var startPoint = me.volumeEnvelope.points[me.volumeEnvelope.sustainPoint];
					if (startPoint) timeOffset = startPoint[0]*tickTime;
					for (var p = me.volumeEnvelope.sustainPoint; p< me.volumeEnvelope.count;p++){
						var point = me.volumeEnvelope.points[p];
						if (point) noteInfo.volumeEnvelope.gain.linearRampToValueAtTime(point[1]/64,time + (point[0]*tickTime) - timeOffset);
					}
				}

				if (me.fadeout){
					var fadeOutTime = (65536/me.fadeout) * tickTime / 2;
					noteInfo.volumeFadeOut.gain.linearRampToValueAtTime(0,time + fadeOutTime);
				}

			}else{
				noteInfo.volumeFadeOut.gain.linearRampToValueAtTime(0,time + 0.1)
			}

            if (me.panningEnvelope.enabled && Audio.usePanning){
                timeOffset = 0;
                startPoint = me.panningEnvelope.points[me.panningEnvelope.sustainPoint];
                if (startPoint) timeOffset = startPoint[0]*tickTime;
                for (p = me.panningEnvelope.sustainPoint; p< me.panningEnvelope.count;p++){
                    point = me.panningEnvelope.points[p];
                    if (point) noteInfo.panningEnvelope.pan.linearRampToValueAtTime((point[1]-32)/32,time + (point[0]*tickTime) - timeOffset);
                }
            }

			return 100;

		}else{
			if (noteInfo.isKey && noteInfo.volume){
				noteInfo.volume.gain.linearRampToValueAtTime(0,time + 0.5)
			}else{
				return 0;
			}
		}

	};

	function processEnvelop(envelope,audioNode,time){
		var tickTime = Tracker.getProperties().tickTime;
		var maxPoint = envelope.sustain ? envelope.sustainPoint+1 : envelope.count;
{'fraction_non_alphanumeric': 0.13976483164083378, 'fraction_numerical': 0.022180652057723144, 'mean_word_length': 3.9438573315719947, 'pattern_counts': {'":': 0, '<': 7, '<?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': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4204989', 'n_tokens_mistral': 2869, 'n_tokens_neox': 2470, 'n_words': 396}