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

Extension plugin support #162

Merged
merged 93 commits into from
Feb 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
b675a89
Modified symbols test-sib
Jun 8, 2020
7af1549
Added symbol styles
Jun 8, 2020
9e058ab
Trying to implement new symbols
Jun 8, 2020
b2cbf42
Changed attributes to Dictionaries
Jun 9, 2020
960b8c8
Solved libmei function call
Jun 9, 2020
a35769a
Correcting dictionaries
Jun 9, 2020
edf045d
Added creation of defined attributes
Jun 9, 2020
f77b1d5
Started with articulations in test-sib
Jun 9, 2020
96b542d
Symbol 52 seems to be a second heel
Jun 9, 2020
e4c4bc6
Added generic modifier export
Jun 9, 2020
c36c1c3
Extract symbol tests
Jun 15, 2020
8c1415c
Reduce redundant/unnecessary code
th-we Jun 15, 2020
db550dd
Text rewrite: Extremely dirty WIP
th-we Jun 15, 2020
67a7ae8
Added tests for every symbol
Jun 15, 2020
1a9e2cf
Load symbolStyles in DoExport()
Jun 15, 2020
43a3594
Fixed mordent test after changing order of symbols in test-sib
Jun 15, 2020
b00aef9
WIP: Make something work
th-we Jun 15, 2020
a08cbce
Reduce redundant/unnecessary code
th-we Jun 15, 2020
d60c15a
Added init of symbol styles to InitGlobals()
Jun 15, 2020
1e6483f
Fixed error in test-sib and added test to check for artics
Jun 15, 2020
fa82059
Text rewrite: Make most things work and cleanup
th-we Jun 15, 2020
87135af
Add text.sib test file (without the actual test code)
th-we Jun 15, 2020
51947fa
Add DataToMEI()
th-we Jun 16, 2020
be680d3
Merge remote-tracking branch 'private/dev_interalSymbols' into dev-ex…
th-we Jun 16, 2020
0c308f6
Prevent spaces at the end of single flag rend attributes
Jun 16, 2020
f7ddf43
Merge branch 'dev-extensions' into HEAD
th-we Jun 16, 2020
9da5205
Merge branch 'wip/text-rewrite' into dev-extensions
Jun 17, 2020
d8d8b50
WIP: text tests... not finished!!!
Jun 17, 2020
413c9e2
Added simple more tests and fixed tempo test
Jun 29, 2020
5153a7d
Started with SymbolHandler adjustments according to TextHandlers
Jun 29, 2020
67cca7d
Merged Modifier & Control Events, created Handler + Symbol Map
Jun 29, 2020
8d32d26
Renamed HandleControlEvents to HandleControlEvent
Jun 30, 2020
f83c8d6
Added Pedal to symbol.sib
Jun 30, 2020
0ad5b88
Changed to SetMethod-Dictionaries and renaming
Jun 30, 2020
b640f59
Using DataToMEI as element factory
Jun 30, 2020
3eee3d7
Add extension detection (no functionality yet)
th-we Jul 3, 2020
df369a5
Add Dialog for extension selection
th-we Jul 3, 2020
c5e4cf9
Draft of extension API
th-we Jul 6, 2020
46e2f8a
Implement extension initialization
th-we Jul 6, 2020
1276b3e
Make extensions work
th-we Jul 7, 2020
db6e9c5
Move initialization of libmei to InitGlobals
Jul 7, 2020
ed093c3
Added simple more tests and fixed tempo test
Jun 29, 2020
1df4619
Merge remote-tracking branch 'origin/test-text' into test-text
Jul 21, 2020
5ecb6bd
Added front matter to test-sib
Jul 27, 2020
3d01a6a
Added reference to MeiFactory in SymbolHandlers
Jul 27, 2020
a1931cd
Renamed TextStyles in TextHandler
Jul 27, 2020
fc618b9
Check for front matter
Jul 27, 2020
4da727a
Check for plain text in measure 2
Jul 27, 2020
501780b
Added tests for text formatting
Jul 27, 2020
d22b6f3
Merge branch 'test-text' into dev-extensions
Jul 27, 2020
4520c1f
Added plain text to output
Jul 27, 2020
d3b08a7
Don't put an <anchoredText> in <tempo>
Jul 27, 2020
16f5a65
Corrected errors in formatting tests
Jul 27, 2020
f31b594
Merge branch 'extension-api-draft' into dev-extensions
th-we Aug 1, 2020
1c55c16
Support extension initialization without showing dialog
th-we Aug 1, 2020
dd5689d
Rename DataToMei to MeiFactory
th-we Aug 1, 2020
854fb93
Replace GenerateFormattedString() with AddFormattedText()
th-we Aug 1, 2020
f2bfcb4
Initialize text handlers in InitGlobals()
th-we Aug 1, 2020
bad3041
Fix text export
th-we Sep 14, 2020
7ec011c
Follow convention to always use Self._property: prefix for global var…
th-we Sep 14, 2020
0e63cb1
Merge pull request #9 from th-we/pr/text-handlers
Sep 14, 2020
de80cae
Updated documentation
Sep 15, 2020
bd53c32
Add text to extensions test & AddFormattedText() available to extensions
Sep 15, 2020
5f7a921
More detailed extension documentation
th-we Sep 16, 2020
fa29b17
Remove unused XMLIdToObjectMap
th-we Sep 15, 2020
2174588
Update re-generated libmei
th-we Sep 15, 2020
09b0c07
Change position of <symbolMap> according to schema
th-we Sep 15, 2020
2391fc4
Fix schema validation problems
th-we Sep 15, 2020
62cfd4a
Deleted doubled function descriptions
Sep 22, 2020
9205f1e
Merge pull request #12 from th-we/pr/extension-doc
Sep 22, 2020
2689419
Added application info for chosen extensions
Sep 22, 2020
1505764
Fixed storage of extension name
Sep 30, 2020
b75c474
Improved readability
Oct 5, 2020
34c96ce
Merge pull request #13 from th-we/dev_extAppInfo
Oct 12, 2020
3423443
Merge branch 'develop' into dev-extensions
th-we Feb 4, 2021
896d436
Branch-independent links in extension documentation
th-we Feb 1, 2021
f41682c
Remove dead code
th-we Feb 4, 2021
fed5a4a
Use better name for ControlEvent function and expose it for extensions
th-we Feb 4, 2021
466e798
Update extension documentation and improve some argument names
th-we Feb 4, 2021
3974fd0
Added basic fermata test
Feb 2, 2021
652e4ce
Checking number of exported fermatas in fermata test
Feb 2, 2021
d962e54
WIP fermatas
th-we Feb 4, 2021
6885f81
WIP fermatas
th-we Feb 4, 2021
8370541
Make fermata/@shape work
th-we Feb 4, 2021
be0e888
fermata/@form tests
th-we Feb 4, 2021
b2938c4
Implement fermata/@form
th-we Feb 4, 2021
f65e5e0
Proper defaults for output file name and folder
th-we Feb 4, 2021
8b93ba8
Close all files before exporting test folder
th-we Feb 5, 2021
19a107f
Trigger mocha tests from ManuScript
th-we Feb 5, 2021
f7fc603
Add a message to example extension
th-we Feb 5, 2021
8edba69
Cosmetics (comments and indentation)
th-we Feb 5, 2021
50240d0
Improve extensions documentation
th-we Feb 5, 2021
9079cc4
Make HandleModifier() fail gracefully when not finding a matching event
th-we Feb 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions Extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Extension API

With extensions to Sibmei, text objects and symbols can be exported in a customized way. This allows addressing custom symbols and text styles and project specific needs.

Extensions are regular Sibelius plugins written in ManuScript. When running Sibmei, it scans for extension plugins. Users can choose which extensions to activate when running Sibmei. Multiple extensions can be activated simultaneously.

![choosing Sibmei extensions](assets/extension-choice.png)

## Example

```js
{
// The `SibmeiExtensionAPIVersion` field must be present so Sibmei can
// recognize compatible extensions
SibmeiExtensionAPIVersion "1.0.0"

Initialize "() {
// The extension choice dialog will list this extension as
// 'Example extension' (the first argument to to AddToPluginsMenu()).
// Second argument can be `null` because an extension plugin does not need a
// `Run()` method.
AddToPluginsMenu('Example extension', null);
}"

// InitSibmeiExtension() is the entry point for Sibmei and must be present
// for Sibmei to recognize an extension plugin.
InitSibmeiExtension "(api) {
// It is recommended to register the api and libmei objects as global
// variables:
Self._property:api = api;
Self._property:libmei = api.libmei;

// Declare which text styles this extension handles
api.RegisterTextHandlers(CreateDictionary(
// Text objects can be matched either by their StyleId or StyleAsText
// property. Here, we match by StyleAsText.
'StyleAsText', CreateDictionary(
// We want the HandleMyText() method to handle Text objects matching
// textObj.StyleAsText = 'My text'
'My text', 'HandleMyText'
)
), Self);
}"

HandleMyText "(_, textObj) {
// Create and return an MEI element that Sibmei will append as a child to
// the measure element.
textElement = api.GenerateControlEvent(textObj, 'AnchoredText');
api.AddFormattedText(textElement, textObj);
return textElement;
}"
}
```

See [another example](./lib/sibmei4_extension_test.plg) for code handling symbols.

## Required Data

### `ExtensionAPIVersion`

A [semantic version string](https://en.wikipedia.org/wiki/Software_versioning#Degree_of_compatibility) specifying for which version of the Sibmei extension
API the extension was written. The current API version of Sibmei can be found in
[`GLOBALS.mss`](./src/GLOBALS.mss).

The API is guaranteed to remain backwards compatible with newer releases that retain the same major version number for `ExtensionAPIVersion`. With minor version numbers, new functionality is added while existing functionality remains backwards compatible.

## Required Methods

### Symbol or Text Handlers

The core purpose of an extension is to define symbol and text handlers to export Sibelius objects in custom ways. (See `HandleMyText()` in the above [example](#example)) These handlers take two arguments:

* `this`: a Dictionary that is passed for technical reasons and *must be ignored by the extension*
* a Sibelius object (`SymbolItem` or `SystemSymbolitem` for symbol handlers, `Text` or `SystemTextItem` for text handlers)

A text handler should return an MEI element (created using libmei) that
Sibmei will append to the `<measure>` element. If `null` is returned instead,
the object will not be exported.

A symbol handler should either call the `HandleModifier()` or `HandleControlEvent()` methods. If neither is called, the object will not be exported. Symbol handlers needn't return anything.

### `InitSibmeiExtension()`

Sibmei calls this method and passes an API Dictionary as argument (see below).
Register your symbol and text handlers in this function using `RegisterSymbolHandlers()` and `RegisterTextHandlers()` (see below).

## API Dictionary

### Interaction with Sibmei

Extensions must only interact with Sibmei through the API dictionary passed to `InitSibmeiExtension()` because Sibmei's core methods may change at any point. If an extension requires access to functionality that is not exposed by the API dictionary, [create an issue](https://github.com/music-encoding/sibmei/issues/new) or a pull request on GitHub.

### API data and methods

The API dictionary exposes the following object:

* **`libmei`**: A reference to libmei that can be used to construct and
manipulate MEI elements. *This dictionary must not be modified.*

It exposes the following methods that must only be called in the initialization phase:

* **`RegisterSymbolHandlers()`**: Call this function to make a symbol handler
known to Sibmei. To tell Sibmei which symbols the extension handles, the symbols must be
registered by their `Index` or `Name` property. For built-in
symbols, always use the `Index` property, for custom symbols, always use the
`Name` property.

The Dictionary that needs to be passed to `RegisterSymbolHandlers()` has the
following structure:

```
CreateDictionary(
'Name', CreateDictionary(
'My custom symbol', 'MyCustomSymbolHandler',
'My other custom symbol', 'MyAlternativeCustomSymbolHandler'
),
'Index', CreateDictionary(
myIndex, 'MyCustomSymbolHandler',
myOtherIndex, 'MyCustomSymbolHandler'
)
)
```

If Sibmei finds a symbol with a `Name` or `Index` property matching a key in
the respective sub-Dictionaries, it will call the symbol handler registered
under that key. A method of that name must be present in the extension
plugin.

If no symbols are registered by either `Name` or `Index` property, the
respective sub-dictionaries can be omitted.

Second argument of `RegisterSymbolHandler()` must be `Self`.

* **`RegisterTextHandlers()`**: Works the same way as
`RegisterSymbolHandlers()`, with the difference that sub-Dictionary keys are
`StyleId` and `StyleAsText` instead of `Index` and `Name`. Always use
`StyleId` for built-in text styles and `StyleAsText` for custom text styles.

The following methods must only be used by handler methods:

* **`MeiFactory()`**: A convenience method that takes a template SparseArray as
argument and generates an MEI element from it. For detailed information, see
the documentation comments in [`Utilities.mss`](./src/Utilities.mss).

It is recommended to define template dictionaries as global variables in the
`InitSibmeiExtension()` method instead of defining them locally in the symbol
handler methods.

* **`HandleControlEvent()`**: Pass this function two arguments:

* The to be exported `SymbolItem` or `SystemSymbolItem`
* A template suitable for passing to `MeiFactory()`

`HandleControlEvent()` creates an MEI element and attaches it to the `<measure>` element. It returns the element for further manipulation by the extension plugin.

* **`HandleModifier()`**: Works similarly to `HandleControlEvent()`, but attaches the generated MEI element to an event element (`<note>`, `<chord>` etc.) instead of the `<measure>` element.

* **`AddFormattedText()`**: Takes arguments:

* `parentElement`: MEI element that the formatted text nodes should be appended to
* `textObj`: A `Text` or `SystemTextItem` object. Its `TextWithFormatting` property is converted to MEI markup.

* **`GenerateControlEvent()`**: Takes two arguments:

* `bobj`: A `BarObject`
* `elementName`: Capitalized MEI element name, e.g. `'Line'`.

Uses the `elementName` to generate an MEI element and adds applicable control event attributes (see `AddControlEventAttributes`)

* **`AddControlEventAttributes()`**: Takes two arguments:

* `bobj`: A `BarObject`
* `element`: An MEI element

Adds the following control event attributes:

* `@startid` (if a start object could be identified) and `@tstamp`
* If applicable (e.g. for lines), `@endid` (if an end object could be identified) and `@tstamp2`
* `@staff` (if object is staff-attached)
* `@layer`
* For lines:
* `@dur.ppq` (unless `Duration` is 0)
* `@startho`, `@startvo`, `@endho`, `@endvo`
* For elements other than lines:
* `@ho`, `@vo`
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,20 @@ These unit tests are primarily used to test specific Sibmei functions. They use

### mocha

[Mocha](https://mochajs.org/) is used to test Sibmei's output from a set of test files. After exporting the test file set with sibmei (Testsibmei will automatically do that), run `npm test` from the root directory of this git repository.
[Mocha](https://mochajs.org/) is used to test Sibmei's output from a set of test files. After exporting the test file set with sibmei (Testsibmei will automatically do that), either run `npm test` from the root directory of this git repository or have Testsibmei automatically trigger the tests. The latter requires a `test.bat` or `test.sh` file in the same directory as the Sibmei `*.plg` files, depending on the operating system. Create a file that looks like this:

#### Windows: test.bat

```
x:
cd x:\path\to\sibmei
cmd /k npm test
```

#### Mac: test.sh

Help for testing and documenting for Mac welcome!

## Writing Extensions

For project specific handling of text and symbols, [extension plugins](Extensions.md) can be written.
Binary file added assets/extension-choice.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading