Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sound and cursor position is not Sync #950

Open
pandya293 opened this issue Sep 28, 2023 · 2 comments
Open

Sound and cursor position is not Sync #950

pandya293 opened this issue Sep 28, 2023 · 2 comments

Comments

@pandya293
Copy link

pandya293 commented Sep 28, 2023

Hello @paulrosen ,

can you please helps me ?
I am attaching
full-synth.txt
Please save file into .html and run in Browser.
Note: using abcjs-basic.js file of version 6.2.2 library

Till 15 bars, sound and cursor is properly synchronise, after 15 bars finished, rhythm sound is come before the cursor reach particular symbol

Please help me to resolve this issue.

also attaching source code
` <title>abcjs: Synth Demo</title>

<link rel="stylesheet" type="text/css" href="../abcjs-audio.css">
<style>
	main {
		max-width: 770px;
		margin: 0 auto;
	}
	.feedback {
		height: 600px;
		font-family: Arial, "sans-serif";
	}
	.highlight {
		fill: #0a9ecc;
	}
	.abcjs-cursor {
		stroke: red;
	}
	.click-explanation {
		color: red;
		font-style: italic;
	}
	.beat {
		font-weight: bold;
	}
	.label {
		color: #888888;
}
	.midi {
		margin-top: 20px;
		margin-left: 5px;
	}
	.seek-controls {
		margin-top: 5px;
	}
	.seek-controls.disabled {
		background-color: #cccccc;
		opacity: 0.5;
	}
</style>

<script src="../dist/abcjs-basic.js" type="text/javascript"></script>
<script type="text/javascript">

	function CursorControl() {
		var self = this;

		self.onReady = function() {
			var downloadLink = document.querySelector(".download");
			downloadLink.addEventListener("click", download);
			downloadLink.setAttribute("style", "");
			var clickEl = document.querySelector(".click-explanation")
			clickEl.setAttribute("style", "");
		};
		self.onStart = function() {
			var svg = document.querySelector("#paper svg");
			var cursor = document.createElementNS("http://www.w3.org/2000/svg", "line");
			cursor.setAttribute("class", "abcjs-cursor");
			cursor.setAttributeNS(null, 'x1', 0);
			cursor.setAttributeNS(null, 'y1', 0);
			cursor.setAttributeNS(null, 'x2', 0);
			cursor.setAttributeNS(null, 'y2', 0);
			svg.appendChild(cursor);

		};
		self.beatSubdivisions = 2;
		self.onBeat = function(beatNumber, totalBeats, totalTime) {
			if (!self.beatDiv)
				self.beatDiv = document.querySelector(".beat");
			self.beatDiv.innerText = "Beat: " + beatNumber + " Total: " + totalBeats + " Total time: " + totalTime;
		};
		self.onEvent = function(ev) {
			if (ev.measureStart && ev.left === null)
				return; // this was the second part of a tie across a measure line. Just ignore it.

			var lastSelection = document.querySelectorAll("#paper svg .highlight");
			for (var k = 0; k < lastSelection.length; k++)
				lastSelection[k].classList.remove("highlight");

			var el = document.querySelector(".feedback").innerHTML = "<div class='label'>Current Note:</div>" + JSON.stringify(ev, null, 4);
			for (var i = 0; i < ev.elements.length; i++ ) {
				var note = ev.elements[i];
				for (var j = 0; j < note.length; j++) {
					note[j].classList.add("highlight");
				}
			}

			var cursor = document.querySelector("#paper svg .abcjs-cursor");
			if (cursor) {
				cursor.setAttribute("x1", ev.left - 2);
				cursor.setAttribute("x2", ev.left - 2);
				cursor.setAttribute("y1", ev.top);
				cursor.setAttribute("y2", ev.top + ev.height);
			}
		};
		self.onFinished = function() {
			var els = document.querySelectorAll("svg .highlight");
			for (var i = 0; i < els.length; i++ ) {
				els[i].classList.remove("highlight");
			}
			var cursor = document.querySelector("#paper svg .abcjs-cursor");
			if (cursor) {
				cursor.setAttribute("x1", 0);
				cursor.setAttribute("x2", 0);
				cursor.setAttribute("y1", 0);
				cursor.setAttribute("y2", 0);
			}
		};
	}

	var cursorControl = new CursorControl();

	var abc = [
	"M: 4/4\n" +
  "L: 1/8\n" +
  "Q:1/4=60\n" +
  "K:clef=perc stafflines = 1\n" +
  "%%barnumbers 1\n" +
  "%%stretchlast 0.6\n" +
  "%%partsbox 1\n" +
  "V:1\n" +
  "A2 z2 (3A4yA2 A2|\n" +
  "(3z2yA4 z2 A2 z2|\n" +
  "(3A2yA4 A2 z2 A2|\n" +
  "z2 A2 (3z2A2A2 z2|\n" +
  "A2 z2 (3A2A2A2 A2|\n" +
  "z2 A2 z2 (3z2z2A2|\n" +
  "A2 z2 (3z2A2A2 A2|\n" +
  "z2 A2 z2 (3z2yA4|\n" +
  "A2 (3A4yA2 z2 A2|\n" +
  "z2 (3A2A2A2 A2 z2|\n" +
  "A2 z2 A2 (3A2yA4|\n" +
  "z2 A2 z2 (3z2z2A2|\n" +
  "A2 (3A4yA2 z2 A2|\n" +
  "z2 (3z2A2A2 A2 z2|\n" +
  "A2 (3A2A2A2 z2 A2|\n" +
  "z2 A2 z2 (3z2yA4|\n" +
  "(3A2yA4 A2 z2 A2|\n" +
  "z2 A2 z2 (3z2z2A2|\n" +
  "A2 (3A4yA2 z2 A2|\n" +
  "z2 A2 z2 (3A2yA4|\n" +
  "A2 z2 (3A2A2A2 A2|\n" +
  "z2 A2 z2 (3z2yA4|\n" +
  "(3z2A2A2 A2 z2 A2|\n" +
  "z2 A2 z2 (3z2z2A2|\n" +
  "A2 (3A2A2A2 z2 A2|\n" +
  "(3z2A2A2 z2 A2 z2|\n" +
  "(3z2yA4 A2 z2 A2|\n" +
  "z2 (3A2yA4 A2 z2|\n" +
  "A2 z2 (3A4yA2 A2|\n" +
  "(3z2z2A2 z2 A2 z2|\n" +
  "(3A2yA4 A2 z2 A2|\n" +
  "(3z2A2A2 z2 A2 z2|\n" +
  "A2 (3A4yA2 z2 A2|\n" +
  "(3z2yA4 z2 A2 z2|\n" +
  "A2 (3A2A2A2 z2 A2|\n" +
  "z2 (3z2z2A2 A2 z2|\n" +
  "A2 z2 A2 (3A2yA4|\n" +
  "z2 A2 z2 (3A2A2A2|\n" +
  "A2 z2 (3z2yA4 A2|\n" +
  "z2 A2 (3A4yA2 z2|\n" +
  "A2 z2 (3z2A2A2 A2|\n" +
  "z2 A2 (3z2z2A2 z2|\n" +
  "A2 (3A2A2A2 z2 A2|\n" +
  "z2 A2 z2 (3A4yA2|\n" +
  "(3z2A2A2 A2 z2 A2|\n" +
  "(3A2yA4 z2 A2 z2|\n"
	];

	var tuneNames = [ "Cooleys", "Bill Bailey", "All Notes On Piano" ];

	var currentTune = 0;

	var synthControl;

	function clickListener(abcElem, tuneNumber, classes, analysis, drag, mouseEvent) {
		var output = "currentTrackMilliseconds: " + abcElem.currentTrackMilliseconds + "<br>" +
			"currentTrackWholeNotes: " + abcElem.currentTrackWholeNotes + "<br>" +
			"midiPitches: " + JSON.stringify(abcElem.midiPitches, null, 4) + "<br>" +
			"gracenotes: " + JSON.stringify(abcElem.gracenotes, null, 4) + "<br>" +
			"midiGraceNotePitches: " + JSON.stringify(abcElem.midiGraceNotePitches, null, 4) + "<br>";
		document.querySelector(".clicked-info").innerHTML = "<div class='label'>Clicked info:</div>" +output;

		var lastClicked = abcElem.midiPitches;
		if (!lastClicked)
			return;

		ABCJS.synth.playEvent(lastClicked, abcElem.midiGraceNotePitches, synthControl.visualObj.millisecondsPerMeasure()).then(function (response) {
			console.log("note played");
		}).catch(function (error) {
			console.log("error playing note", error);
		});
	}

	var abcOptions = {
		add_classes: true,
		clickListener: self.clickListener,
		responsive: "resize"
	};

	function load() {
		document.querySelector(".next").addEventListener("click", next);
		document.querySelector(".start").addEventListener("click", start);
		document.querySelector(".warp").addEventListener("click", warp);
		document.querySelector(".seek").addEventListener("click", seek);
		document.querySelector(".seek2").addEventListener("click", seek2);
		document.querySelector("#seek-units").addEventListener("change", seekExplanation);

		if (ABCJS.synth.supportsAudio()) {
			synthControl = new ABCJS.synth.SynthController();
			synthControl.load("#audio", cursorControl, {displayLoop: true, displayRestart: true, displayPlay: true, displayProgress: true, displayWarp: true});
		} else {
			document.querySelector("#audio").innerHTML = "<div class='audio-error'>Audio is not supported in this browser.</div>";
		}
		setTune(false);
	}

	function download() {
		if (synthControl)
			synthControl.download(tuneNames[currentTune] + ".wav");
	}

	function start() {
		if (synthControl)
			synthControl.play();
	}

	function seek() {
		synthControl.seek(0.50)
	}

	function seekExplanation() {
		var explanation = document.getElementById("unit-explanation");
		if (!synthControl.visualObj.noteTimings) {
			explanation.innerText = "First start playing to load audio before seeking.";
			return;
		}
		var units = this.value;
		var max = 1;
		switch (units) {
			case "seconds":
				max = synthControl.visualObj.getTotalTime();
				break;
			case "beats":
				max = synthControl.visualObj.getTotalBeats();
				break;
		}
		explanation.innerText = "Enter a number between 0 and {0}.".replace("{0}", max);
	}

	function seek2() {
		var amount = document.getElementById("seek-amount").value;
		var units = document.getElementById("seek-units").value;
		synthControl.seek(amount, units)
	}

	function warp() {
		var el = document.querySelector(".warp");
		el.setAttribute("disabled", true)
		var amount = Math.random()
		console.log("warp", amount)
		synthControl.setWarp(amount*100).then(function () {
			el.removeAttribute("disabled")
		})
	}

	function setTune(userAction) {
		var seekControls = document.querySelector(".seek-controls");
		seekControls.classList.add("disabled");
		// synthControl.disable(true);
		var visualObj = ABCJS.renderAbc("paper", abc[currentTune], abcOptions)[0];
		var midi = ABCJS.synth.getMidiFile(abc[currentTune]);
		var midiButton = document.querySelector(".midi");
		midiButton.innerHTML = midi;

		// TODO-PER: This will allow the callback function to have access to timing info - this should be incorporated into the render at some point.
		var midiBuffer = new ABCJS.synth.CreateSynth();
		midiBuffer.init({
			//audioContext: new AudioContext(),
			visualObj: visualObj,
			// sequence: [],
			// millisecondsPerMeasure: 1000,
			// debugCallback: function(message) { console.log(message) },
			options: {
				// soundFontUrl: "https://paulrosen.github.io/midi-js-soundfonts/FluidR3_GM/" ,
				// sequenceCallback: function(noteMapTracks, callbackContext) { return noteMapTracks; },
				// callbackContext: this,
				// onEnded: function(callbackContext),
				// pan: [ -0.5, 0.5 ]
			}
		}).then(function (response) {
			console.log(response);
			if (synthControl) {
				synthControl.setTune(visualObj, userAction).then(function (response) {
					console.log("Audio successfully loaded.")
					seekControls.classList.remove("disabled");
					seekExplanation();
				}).catch(function (error) {
					console.warn("Audio problem:", error);
				});
			}
		}).catch(function (error) {
			console.warn("Audio problem:", error);
		});
	}

	function next() {
		currentTune++;
		if (currentTune >= abc.length)
			currentTune = 0;
		setTune(true);
	}


</script>

`

@paulrosen
Copy link
Owner

It looks like there might be a bug but I'm not sure where. Can you make the smallest reproduceable example in https://editor.drawthedots.com?

I suspect it is either related to the "y" spacers (but that seems to work for me in other pieces) or something to do with the triplets. Or perhaps when you have a "y" spacer inside a triplet?

@pandya293
Copy link
Author

pandya293 commented Oct 3, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants