[DevLog] Building Show Notes 2

Hey All,

I’ve finally settled on an approach for a new version of Cantabile’s Show Notes and rather than develop it in the dark, I thought I’d post progress updates here for anyone interested in following along.

I’ve put this off for a long time because the requirements have be pretty wide and varied but the time has come to start work on this… and the experiments with cantabile-media-server, especially the PDF support has solidified the approach I want to take with this - which is to build it using web technologies (nodejs, browser, html, javascript, markdown, WebSockets, etc…)

In case you’re worried: this new approach won’t be replacing the existing show notes anytime soon - the intention is to allow both the old and new approaches to be available for at least a considerable transition period (if not indefinitely).

These posts will be a bit of a technical discussion, but not so much that you shouldn’t be able to get the general of idea of where this is going.

As always, comments and feedback welcome.

Brad

7 Likes

I’m looking forward to the new version. Thanks for letting us in on it!

2 Likes

Background

See these posts for some background thoughts, suggestions, ideas etc…

Requirements

Distilling all of the above down, this is what I’m aiming for as the main requirements:

  • Improved text formatting (eg: ability to color individual words, in paragraph formatting etc…)
  • Integration with Cantabile’s states (eg: show/hide sections based on state, auto scroll to state marker)
  • Ability to embed cantabile $(Variables) in notes.
  • Support for images
  • Support for PDF files (or parts thereof)
  • Support for multi-column layout and/or tables
  • Other formatting features like bullet lists, numbered lists, quotations etc…
  • Ability to view notes both within Cantabile and remotely via Web-UI. (eg: viewing notes on a tablet)
  • Ability to deliver different notes to different band members
  • Support for ChordPro display
  • Support for auto-scrolling based on song timing and/or bindings
  • Support for manual scrolling via MIDI control
  • Ability to convert old show notes to new system

Basic Approach

Here’s initial approach I’ll be taking with this:

  • Show notes will become a plain text file format
  • The notes will be stored as a separate file next to the associated song file. (eg: MySong.cantabileSong and MySong.cantabileNotes live together)
  • Editing will be done using an external text editor of your choice
  • The format will be markdown, with embedded directives for additional control and markup
  • The notes will be displayed in a separate web browser
  • Integration with Cantabile will be via the Cantabile Network API.
  • PDF rendering using Ghostscript

Later:

  • Support for storing the notes content embedded in Cantabile (rather than as a separate file)
  • Support for editing the notes directly in Cantabile (rather than with a separate editor)
  • Embed a web browser in Cantabile so notes can be viewed in-place without requiring a separate window.
  • Direct integration between the embedded browser and Cantabile (rather than via the network API).

Proof of Concept

Over the last couple of weeks, cantabile-media-server has proved to be a good proof of concept for this approach:

  • Works as a media server
  • MIDI controllable from Cantabile
  • WebSocket support lets Cantabile control playback and scrolling within the browser.

Given the additional text formatting requirements for show notes I think doing it in HTML/JavaScript and rendering in a browser is a good approach - there’s just one problem: many users don’t want their show notes in a separate browser window and would prefer them displaying inside Cantabile.

There’s two solutions to this:

  1. Build it twice, once embedded in Cantabile and once in a browser.
  2. Embed a browser directly inside Cantabile and build it just once.

I really don’t want to build this twice, so today’s job was to see if a browser can be easily embedded in Cantabile. Turns out it can - see here. (ignore the “Browser” tab that’s just a placeholder for this test).

That was the final piece in deciding on this approach. The following posts will be focused on the show notes format and will be demoed in an external browser - just remember the idea is to eventually move this so it can all run inside Cantabile.

Next job - start implementing the show notes Markdown + directives file format and get it appearing in a browser.

3 Likes

I do not use show notes much, but excited for those who do

1 Like

Hi @brad, excited to see how this works out. Any thoughts on supporting MusicXML or even ABC format?

1 Like

No promises but both are possibilities.

The main thing is I don’t want to get into parsing and rendering those formats myself, but if there are libraries or tools available then it should be possible to integrate them.

Some possibilities:

Basic Markdown Rendering

OK, nothing too thrilling here, but I’ve got to start somewhere… Markdown rendering working:

(source notes file at the top, converted markdown at the bottom)

Next up: directive parsing, starting with color formatting.

3 Likes

Styling

Markdown is great for converting plain text to HTML with some formatting control, but it doesn’t provide any real styling ability (eg: fonts, colors, layouts etc…) (because that’s not what it was designed for).

Cantabile’s show notes really need this though, so I’ve invented a simple syntax for “directives” which are just lines that start with an exclamation point.

Firstly, lines starting with !! are comments and are ignored.

Next, these directives set various color and layout controls. The value is anything that’s accepted by css. (eg: !color red and !color #FF0000 and !color #F00 are all equivalent)

  • !color
  • !background-color
  • !padding
  • !margin
  • !text-align
  • !font-size
  • !font-family

The reset directive clears any previously set styles.

  • !reset

Finally, for the power users, the !style directive lets you specify multiple styles on a single line and also supports styles for which there’s not specific directive above. These styles are merged over any other active styles.

Here’s an example:

# Styling Tests

!! Lines that start with two exclamation points are comments and will be ignored

!! Lines that start with an exclamation point are "directives" that provide additional 
!! styling and formatting control over and above what straight Markdown provides.

!color lime
!background-color #444
!padding 5px 50px
!margin 0 50px
!text-align center
!font-size 22pt
!font-family Times New Roman

A colored paragraph with padding and margins

And a second paragraph with same style

!! The reset directive clears all previously assigned styling instructions
!reset

!! If you're a html/css tech head, you can also write styles directly:
!style color: red; background-color: #222; font-size: 16pt;
Warning Warning Warning!!!
!reset

Back to normal here!

This screen shot shows source notes format at top left, the result at bottom left and (for the curious) the generated HTML elements on the right.

2 Likes

Thanks Brad. A couple of years ago hacked the web interface to include abcjs support, didn’t take long even with my very rusty JS skills. Basically any note that was “fixed” font was rendered using it :grinning:

1 Like

@johncarter good to know that was relatively simple to integrate.

Conditional Directives (Part 1)

Conditional directives are are probably going to be one of the trickier parts of this new show notes format.

These are required to emulate the state control that Cantabile has that allows notes to change when switching states:

image

In the new show notes this functionality will be done with conditional directives: !if, !else, !elseif and !endif.

For example:

!if state == "Intro"
Text to display when state is "Intro"
!else if state == "Outro"
Text to display when state is "Outro"
!else
Text to display for all other states.
!endif

Warning: tech geekery incoming…

The tricky part is that I don’t want the entire document to be reprocessed and recreated from scratch when a state changes. Instead, it needs to update the existing content hiding and showing parts as required.

eg: the above would probably end up something like this with JavaScript to toggle the display:none style on each branch depending on the conditions.

<div><p>Text to display when state is "Intro"</p></div>
<div style="display:none"><p>Text to display when state is "Outro"</p></div>
<div style="display:none"><p>Text to display for all other states.</p></div>

But, it gets trickier because conditional blocks might be used just for styling:

!if state == "Intro"
!color red
!else
!color blue
!endif
Some Text

which would result in one paragraph “Some Text” and Javascript to update the color style to either red or blue. ie: this

<div style="color:red"><p>Some Text</p></div>

or this:

<div style="color:blue"><p>Some Text</p></div>

Then there’s nested conditional directives and mixes of style + text changes etc…

Building an Abstract Syntax Tree (kindof)

Anyway, I figured the first step in all this is converting the flat list of if/else/endif/endif directives into a tree of conditional blocks. This will be used both to insert the appropriate divs around conditional sections and also to generate the Javascript to update it on the fly.

The first pass over the input text (the bit I wrote this morning) splits the file into sections of markdown text interleaved with directives giving a flat array of things like this:

apples
!if condition
pears
!elseif otherCondition
watermelon
!if nestedCondition
strawberries
!else
blueberries
!endif
!else
bananas
!endif

The second pass converts the conditional directives into a hierarchy:

[
    "apples",
    {
        "if": "condition",
        "trueBlock": [
            "pears"
        ],
        "falseBlock": [
            {
                "if": "otherCondition",
                "trueBlock": [
                    "watermelon",
                    {
                        "if": "nestedCondition",
                        "trueBlock": [
                            "strawberries"
                        ],
                        "falseBlock": [
                            "blueberries"
                        ]
                    }
                ],
                "falseBlock": [
                    "bananas"
                ]
            }
        ]
    }
]

Notice how the !elseif directive gets converted to a nested !if block reducing everything to simpler if/else conditions.

The next step will be to do some control flow analysis over this tree to work out which sections might be shown/hidden and which styles can change and which are fixed.

2 Likes

Really looking forward to this update, thanks for sharing your development diary.

Now we can browse the forum directly in Cantabile! :smiley:

Concerning these points, is the plan to always support an external browser? Thinking about the other band members use case but also some people might want the notes in a separate window or integrated into the WebUI.

And the font colouring, can that be per word or is it limited to a paragraph? Could have the style applied to a <span> instead of <div> in this case.

Thanks!

In theory, but there probably won’t be an address bar as such. I expect it’ll be more locked down than that.

Yes definitely. Remote view on other PCs/tablets is a big part of this.

Both. So far, I’ve implemented paragraph coloring, but inline styling is planned. In theory, you should already be able to You can already do this with inline HTML spans through Markdown but I hope to provide a simple syntax for this.

3 Likes

You’ve woken up my spirit, looking forward Brad :star_struck:

2 Likes

Hi @So_Godly

I know you’ve been waiting a long time for some improvements to show notes, but I’m glad I held off this long. This new approach is going to be much better than what I had planned back then.

Brad

3 Likes

Conditional Directives (Part 2)

Conditional text include/exclude working:

Demo Video

eg:

Next up… conditional styling directives.

3 Likes

Hang on, this mightn’t be the best approach

One of the limitations with the approach I’ve chosen for this is that you can’t use directives inside multi-line Markdown structures.

eg:

image

The reason for this is because I’m processing the text between directives as standalone markdown blocks. ie: the directives cause the start and end of the structure to be separated and the markdown isn’t generated correctly.

I was aware of this when I started on this approach but didn’t think it would be much of a problem. But the more I think about it, the more I’m worried because it’s unexpected behavour and fairly easy to get caught out.

I’m going to take a step back and see if I can come up with a better way.

After thinking about this problem some more, I’ve decided to ignore it - for the moment at least.

  1. It’s not that big of an issue.
  2. Making it better is not trivial - I can’t see a way to make this work with an existing Markdown processor - even given marked.js extensibility and even if I wrote my own markdown engine from scratch - it’s still a difficult problem.
  3. The benefits of using an out-of-the-box markdown processor far outweigh the downsides of these weird edge cases.
  4. If it does turn out to be an issue I can deal with it later - remaining work on this project still needs to be done either way.

So, I’m pressing on regardless. Today’s job it to get conditional directives working with styling directives.

3 Likes

Conditional Directives (Part 3)

Conditional Styling is now working… watch demo.

example:

Coming along nicely. Next up is… the weekend :slight_smile:

Brad

5 Likes