File size: 8,381 Bytes
af6912c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# Timing Callbacks

This runs an animation timer and does callbacks at various intervals. This allows you to do various effects that are timed with beats or playing notes.

## Creation

To use this create an instance of it:
```javascript
var timingCallbacks = new abcjs.TimingCallbacks(visualObj, params);
```

| Parameters | Description |
| ------------- | ----------- |
| visualObj | This is the output of the `renderAbc()` call. It is the music that will be timed. |
| params | This is a object. See below for the possible properties. |

## Parameters

| Name | Default | Description |
| ------------- | ------- | ----------- |
| `qpm` | whatever is in the Q: field | Number of beats per minute. |
| `extraMeasuresAtBeginning` | 0 | Don't start the callbacks right away, but insert this number of measures first. |
| `beatCallback` | null | Called for each beat passing the beat number (starting at 0). |
| `eventCallback` | null | Called for each event (either a note, a rest, or a chord, and notes in separate voices are grouped together.) |
| `lineEndCallback` | null | Called at the end of each line. (This is useful if you want to be sure the music is scrolled into view at the right time.) See `lineEndAnticipation` for more details. |
| `lineEndAnticipation` | 0 | The number of milliseconds for the `lineEndCallback` to anticipate end of the line. That is, if you want to get the callback half a second before the end of the line, use 500. |
| `beatSubdivisions` | 1 | How many callbacks should happen for each beat. This allows finer control in the client, for instance, to handle a progress bar. |

## Callbacks

### beatCallback

This is called once for every beat in the tune. It is called one additional time when the tune is finished.

```javascript
function beatCallback(beatNumber, totalBeats, totalTime, position, debugInfo) {}
```

|Name|Description|
|---|---|
| beatNumber | Zero-based beat number. Usually this will increment sequentially and regularly, but if javascript is paused long enough (for instance, if the browser tab is changed), then there may be a number of these calls at once when it catches up. |
| totalBeats | The total number of beats (including all repeats) that will be played. |
| totalTime | The total number of milliseconds of the tune. |
| position | The interpolated position of the cursor if the beat occurs between notes. This is an object with the attributes { left: , top: , height: } This can be used to smooth out the cursor by moving it on the beat callbacks. The higher the number of `beatSubdivisions` the smoother the cursor will be. |
| debugInfo | A hash of some extra info that might be useful in figuring out why the callback was triggered. |

### eventCallback

This is called once for every "event" in time - either a note or a rest. If there are multiple notes at the same time, then it is only called once
for that group of notes.

```javascript
function eventCallback(ev) {}
```

The parameter `ev` is an object that looks like this:

```javascript
ev = {
    "type": "event", // This is always "event"

    "milliseconds": number, // The number of milliseconds from the beginning of the piece
    "millisecondsPerMeasure": number, // The number of milliseconds per measure

    "line": number, // The current "line", that is, the staff system.
    "measureNumber": number, // The measure number. Resets per line, so the first measure number on a line is zero.

    "top": number, // The number of pixels from the top of the svg that the note appears.
    "height": number, // The height of the note, in pixels. 
    "left": number, // The number of pixels from the left edge of the svg.
    "width": number, // The width of the note

    "elements": [  ], // Array of the actual elements on the page that are represented by the note or notes.
    "startCharArray": [ number ], // the character position in the original abc string
    "endCharArray": [ number ], // the character position in the original abc string
    "midiPitches": [ // Array of the currently playing pitches
        {
            "pitch": number, // The pitch number (based on the midi standard, i.e. middle C is 60)
            "durationInMeasures": number, // the note value as a fraction. (that is, a quarter note is 0.025)
            "volume": number, // The volume expressed as a number between 0 and 127
            "instrument": number // The instrument number (based on the midi standard, i.e. acoustic_grand_piano is 0)
        }
    ]
}
```

#### Notes:

* The `startCharArray` and `endCharArray` are arrays because there is more than one location in the abc string if there is more than one voice.

* The format of the `elements` array is subject to change in future versions.

* This is called one last time with passing in `null` at the end of the tune. On that call `eventCallback` can return the string "continue" to keep the timer from stopping. This is useful if you want to play on repeat - in theory you would probably have another call to `seek()`.

* This function can be a Promise or not.

### lineEndCallback

This will be called as the cursor is approaching the end of a line of music. This is useful if there is more than a screen's worth of music; it can be used to scroll the page at the right time.

```javascript
function lineEndCallback(info, event, details) {}
```

The parameter `info` looks like this:

```javascript
info = {
    "milliseconds": number, // current milliseconds from beginning of piece
    "top": number, // The number of pixels from the top of the svg to the top of the cursor
    "bottom": number // The number of pixels from the top of the svg to the bottom of the cursor
}
```
The parameter `event` is the standard note event.

The parameter `details` looks like this:
```javascript
details = {
    "line": number, // the current line number (zero-based)
    "endTimings": array // the array of the timings for each line
}
```
The `endTimings` array elements are of the same type as the `info` parameter.

## Functions

These are the entry points that can be called on the `timingCallbacks` object.

### start(position, units)

This starts the timer that triggers the callbacks. This is called to both start and resume after calling pause. See the `setProgress` method below for explanation of the parameters with one special case:

If `position` is undefined then if the previous call was to `pause()`, then the animation continues from where it left off. If there was no pause, then the animation starts from the beginning.

### pause()

Pauses the animation. Calling `start()` afterwards will resume from where it left off.

### stop()

Stop the animation. After calling this, the next call to `start()` will start at the beginning.

### reset()

Move the timer back to the beginning, so the animation starts over. This can be called either when the animation is currently running or when it is paused.

### setProgress(position, units)

Change the position of the animation. This allows random access to any place in the tune. 

If the second parameter is not present, then `units` equals "percent". The possible values are:

* `"percent"`: The percent passed in is a number between 0 and 1. This can be called either when the animation is currently running or when it is paused. 

* `"seconds"`: The seconds from the beginning of the tune. If this is passed the end of the tune it is changed to the end.

* `"beats"`: The beats from the beginning of the tune. If this is passed the end of the tune it is changed to the end.

### replaceTarget(visualObj)

If the underlying music changes on the fly, this replaces the current object without having to destroy the object and start over. `visualObj` is the return value from `renderAbc`.

## Example

Paste in any ABC you want here then click "start" to see what is returned by the timing callbacks:

<example-tune-book v-if="abcjsReady" :callbacks="callbacks" :tune-id="32"></example-tune-book>

<timing-callbacks ref="timingCallbacks" target="#abc"></timing-callbacks>

<script>
	import { waitForAbcjs } from '../../../wait-for-abcjs';
	export default {
		async mounted() {
            await waitForAbcjs()
            this.abcjsReady = true;
			setTimeout(() => {
				this.callbacks = [this.$refs.timingCallbacks];
			}, 500);
		},
		data() {
			return {
                abcjsReady: false,
				callbacks: [],
			};
		},
	}
</script>