0

Trying the have the cursor move on the sheet.

The Angular 7 component is:

  public createSheet(name: string) {
    // tslint:disable-next-line: max-line-length
    const tunes = 'T: Cooley\'s\n' +
    'M: 4/4\n' +
    'L: 1/8\n' +
    'R: reel\n' +
    'K: Emin\n' +
    '|:D2|EB{c}BA B2 EB|~B2 AB dBAG|FDAD BDAD|FDAD dAFD|\n' +
    'EBBA B2 EB|B2 AB defg|afe^c dBAF|DEFD E2:|\n' +
    '|:gf|eB B2 efge|eB B2 gedB|A2 FA DAFA|A2 FA defg|\n' +
    'eB B2 eBgB|eB B2 defg|afe^c dBAF|DEFD E2:|';

    const sheet = abcjs.renderAbc(name, tunes, { add_classes: true });
    setTimeout(() => {
      abcjs.startAnimation(name, sheet[0], { showCursor: true });
    }, 2000);
    return sheet;
  }

with the CSS content:

.cursor-editor {
  width: 500px;
  margin-top: 12px;
}
.cursor-nav {
  margin-bottom: 12px;
}
.abcjs-cursor {
  background-color: rgba(196,196,196,.5);
}
.cursor {
    background-color: #ffffc0;
    opacity: 0.5;
    border-left: 1px solid black;
}

But it gives me the error:

core.js:15724 ERROR TypeError: paper.querySelector is not a function
    at Object.push.../../node_modules/abcjs/src/api/abc_animation.js.animation.startAnimation (abc_animation.js:38)
    at midi-lib.js:1457

with the source code line being:

if (options.showCursor && !paper.querySelector('.abcjs-cursor')) {

When looking at the source code I can see that the parameter paper is supposed to be an object and not a string:

animation.startAnimation = function(paper, tune, options) {
  if (timer) {
    timer.stop();
    timer = undefined;
  }

  if (options.showCursor && !paper.querySelector('.abcjs-cursor')) {
    cursor = document.createElement('DIV');

But unless I'm wrong, the animation example given shows the usage of a string.

Indeed, looking at the following source code I could see the following:

console.error("ABCJS.startAnimation: When using scrollHorizontal/scrollVertical/scrollHint, the music must have been rendered using viewportHorizontal/viewportVertical.");else console.error("ABCJS.startAnimation: The second parameter must be a single tune. (Did you pass the entire array of tunes?)");else console.error("ABCJS.startAnimation: The first parameter must be a regular DOM element. (Did you pass a jQuery object or an ID?)");

But the above given example seems to contradict these error messages:

const tunes = abcjs.renderAbc("paper", abcString, { add_classes: true });

abcjs.startAnimation("paper", tunes[0], {
    showCursor: true,
});

The first parameter is not the DOM element but its id attribute.

Now, I'll try to find out what that argument should be in my case.

UPDATE: I could get the startAnimation call without error, but still, after clearing the browser cache, there is no moving cursor.

setTimeout(() => {
  const element = document.getElementById(name);
  console.log(element);
  abcjs.startAnimation(element, sheet[0], { showCursor: true });
  console.log('Started the animation');
}, 2000);

Alternatively, is there a method to add one or several tunes to the array of tunes after the rendering has started ? So as to have new notes appear on the sheet, on a stave that displays in one line without break, and the oldest tunes disappear, say a method like abcjs.addTunes(element, sheet[0]);

UPDATE: I can see a moving animated cursor in this example page I did, but still not in my Angular application.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" href="favicon.ico" type="image/x-icon" />
    <script src="abcjs_midi_5.6.11-min.js" type="text/javascript"></script>
    <style media="screen" type="text/css">
    .abcjs-cursor {
      background-color: rgba(196,196,196,.5);
    }
    .cursor {
      background-color: #ffffc0;
      opacity: 0.5;
      border-left: 1px solid black;
    }
    </style>
  </head>
  <body>
    <div id="paper"></div>

    <script type="text/javascript">
var elementName = "paper";
var abcString = 'T: Cooley\'s\n' +
'M: 4/4\n' +
'L: 1/8\n' +
'R: reel\n' +
'K: Emin\n' +
'|:D2|EB{c}BA B2 EB|~B2 AB dBAG|FDAD BDAD|FDAD dAFD|\n' +
'EBBA B2 EB|B2 AB defg|afe^c dBAF|DEFD E2:|\n' +
'|:gf|eB B2 efge|eB B2 gedB|A2 FA DAFA|A2 FA defg|\n' +
'eB B2 eBgB|eB B2 defg|afe^c dBAF|DEFD E2:|';
console.log(elementName);
const tunes = ABCJS.renderAbc(elementName, abcString, { add_classes: true });
var element = document.getElementById(elementName);
console.log(element);
ABCJS.startAnimation(element, tunes[0], { showCursor: true, });
    </script>
  </body>
</html>
Stephane
  • 11,836
  • 25
  • 112
  • 175

2 Answers2

0

There is a newer way to do animation using the TimingCallbacks method that is more flexible. (search for timingParams in https://github.com/paulrosen/abcjs/blob/master/docs/api.md )

var timer = new abcjs.TimingCallbacks(sheet[0], {
  beatCallback: this.beatCallbackIntercept,
  eventCallback: this.eventCallbackIntercept,
  lineEndCallback: this.lineEndCallbackIntercept,
});

timer.start();

Then you are responsible for doing whatever drawing you want in the callbacks. For instance:

function initCursor() {
  var cursor = document.createElementNS(svgNS, "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);
  const svg = document.querySelector("#paper svg");
  svg.appendChild(cursor);
}

function setCursor(range) {
  if (range.measureStart && range.left === null)
    return; // this was the second part of a tie across a measure line. Just ignore it.
  let cursor = document.querySelector(".abcjs-cursor");
  cursor.setAttribute("x1", range.left-2);
  cursor.setAttribute("x2", range.left-2);
  cursor.setAttribute("y1", range.top);
  cursor.setAttribute("y2", range.top + range.height);
}

For your second question: there isn't a way to add music after the fact, you have to redraw, but I've found the redrawing is pretty fast. If you are adding entire tunes, I would just create more divs to place the music in instead of redrawing all of the tunes.

The above method of animation can swap out tunes on the fly, so the cursor will move smoothly:

timer.replaceTarget(sheet[0])
Paulie
  • 1,940
  • 3
  • 20
  • 34
0

I managed to draw the cursor but it isn't an official fix I think.

After the abcjs initialized in the ngOnInit method:

this.testSheet = abcjs.renderAbc('test', this.notes, {scrollHorizontal: true});

I managed to add my custom cursor for the test element like this:

const sheet = document.getElementById('test');
const cursor = document.createElement('div');
cursor.className = 'abcjs-cursor';
sheet.style.position = 'relative';
cursor.style.background = 'red';
cursor.style.position = 'absolute';
sheet.appendChild(cursor);

As you can see, you can format the cursor as you want. I have to add the cursor like this, because the abcjs checks if the cursor exists in the DOM. If not, then it will add its own cursor

abc_animation.js line 38:

if (options.showCursor) {
        cursor = paper.querySelector('.abcjs-cursor');
        if (!cursor) {
            cursor = document.createElement('DIV');
            cursor.className = 'abcjs-cursor cursor';
            cursor.style.position = 'absolute';

            paper.appendChild(cursor);
            paper.style.position = 'relative';
        }
    }

here is the Component.html file:

<button (click)="start()">Play</button>
<div id="test"></div>

and here is the start() method:

start() {
  const element = document.getElementById('test');
  abcjs.startAnimation(element, this.testSheet[0], {showCursor: true});
}

It's not the best practice in Angular I think, but hey! It works! (For me)

cs.matyi
  • 1,204
  • 1
  • 11
  • 21