Server-side rendering in ASP.NET MVC

You can use Manufaktura.Controls library for creating SVG or HTML canvas markup code for rendering score objects. The code is generated on server side by HtmlCanvasScoreRender or HtmlSvgScoreRenderer and then returned to the client as a string in web response. The generated code doesn’t contain any music information – only geometric shapes and some html attributes for midi playback which are described later.

Manufaktura.Controls.AspNetMvc contains useful extensions that allow you tu use Razor syntax instead of calling renderers directly.

Create  a new ASP.NET MVC project in Visual Studio (version for ASP.NET Core is still in development and currently unsupported). First create a folder for fonts and add at least one format of Polihymnia font to the project:

  • Polihymnia.svg
  • Polihymnia.ttf
  • Polihymnia.woff

You should also add references to the following libraries:

  • Manufaktura.Music
  • Manufaktura.Controls
  • Manufaktura.Controls.AspNetMvc

For .NET Core use Manufaktura.Controls.AspNetCore instead of Manufaktura.Controls.AspNetMvc.

Then create a sample viewmodel:

using Manufaktura.Controls.Model;

using Manufaktura.Controls.Model.Fonts;

using Manufaktura.Controls.Rendering.Implementations;

 

namespace Manufaktura.Examples.NoteViewerWebMvc.ViewModels

{

    public class ScoreViewModel

    {

        public ScoreViewModel()

        {

            var musicFontUris = new[] { "/fonts/Polihymnia.svg", "/fonts/Polihymnia.ttf", "/fonts/Polihymnia.woff" };

            Settings = new HtmlScoreRendererSettings

            {

                RenderSurface = HtmlScoreRendererSettings.HtmlRenderSurface.Svg,

                RenderingMode = Controls.Rendering.ScoreRenderingModes.AllPages

            };

            Settings.Fonts.Add(MusicFontStyles.MusicFont, new HtmlFontInfo("Polihymnia", 22, musicFontUris));

            Settings.Fonts.Add(MusicFontStyles.StaffFont, new HtmlFontInfo("Polihymnia", 24, musicFontUris));

            Settings.Fonts.Add(MusicFontStyles.GraceNoteFont, new HtmlFontInfo("Polihymnia", 12, musicFontUris));

            Settings.Fonts.Add(MusicFontStyles.LyricsFont, new HtmlFontInfo("Open Sans", 9, "/fonts/OpenSans-Regular.ttf"));

            Settings.Fonts.Add(MusicFontStyles.TimeSignatureFont, new HtmlFontInfo("Open Sans", 12, "/fonts/OpenSans-Regular.ttf"));

            Settings.Fonts.Add(MusicFontStyles.DirectionFont, new HtmlFontInfo("Open Sans", 10, "/fonts/OpenSans-Regular.ttf"));

        }

 

        public Score ExampleScore { get; set; }

        public HtmlScoreRendererSettings Settings { get; private set; }

    }

}

The ScoreViewModel class contains two properties:

  • ExampleScore – score object to render,
  • Settings – HTML renderer settings.

It is obligatory to set paths for fonts and font sizes similar to the above example.

The other useful settings are:

  • RenderSurface – determines if scores will be rendered as Svg or Canvas (Svg is betrer supported),
  • RenderingMode – determines rendering mode:
    • AllPages – score is rendered normally with all page breaks and system breaks,
    • SinglePage – only one page is rendered,
    • Panorama – score is rendered without page breaks or system breaks.

Now add controller action to return a view with sample score:

        public ActionResult Index()

        {

            var score = Score.CreateOneStaffScore(Clef.Treble, MajorScale.G);

            score.FirstStaff.AddRange(StaffBuilder

                .FromPitches(Pitch.G4, Pitch.B4, Pitch.D5, Pitch.C4, Pitch.E4, Pitch.G4, Pitch.D4, Pitch.DSharp4, Pitch.A4)

                .AddUniformRhythm(4));

            return View(new ScoreViewModel { ExampleScore = score });

        }

In .NET Core it should return IActionResult instead of ActionResult.

Now create view Index.cshtml:

@model Manufaktura.Examples.NoteViewerWebMvc.ViewModels.ScoreViewModel

@using Manufaktura.Controls.AspNetMvc <!-- For .net core use Manufaktura.Controls.AspNetCore -->

 

<div>

    @Html.NoteViewerFor(x => x.ExampleScore, x => x.Settings)

</div>

You can now run your project. On ASP.NET MVC it should run without problems.

 

Midi Playback

Html code generated by SvgHtmlScoreRenderer contains three special html attributes that can be used to implement playback:

<text x=”83.5” y=”256.9” style=”font-color:rgb(0,0,0); font-size:22pt; font-family: Polihymnia;” id=”scoreElement_10608” data-playback-start=”0” data-playback-duration=”2000” data-midi-pitch=”53”>wj</text>

  • data-playback-start – time (in milliseconds) when playback starts,
  • data-playback-durarion – duration (in milliseconds) of element,
  • data-midi-pitch – midi pitch

These attributes can be used to implement midi playback with third party MIDI library like midi.js (https://github.com/mudcube/MIDI.js/).

First you have to reference these libraries in a view:

<script src="~/Scripts/midijs/Base64.js" type="text/javascript"></script>

<script src="~/Scripts/midijs/Base64binary.js" type="text/javascript"></script>

<script src="~/Scripts/midijs/WebAudioAPI.js" type="text/javascript"></script>

<script src="/Scripts/midijs/audioDetect.js" type="text/javascript"></script>

<script src="/Scripts/midijs/gm.js" type="text/javascript"></script>

<script src="/Scripts/midijs/loader.js" type="text/javascript"></script>

<script src="/Scripts/midijs/plugin.audiotag.js" type="text/javascript"></script>

<script src="/Scripts/midijs/plugin.webaudio.js" type="text/javascript"></script>

<script src="/Scripts/midijs/plugin.webmidi.js" type="text/javascript"></script>

<script src="/Scripts/midijs/dom_request_xhr.js" type="text/javascript"></script>

<script src="/Scripts/midijs/dom_request_script.js" type="text/javascript"></script>

Then create a viewmodel for playback:

var ClientSongViewModel = function () {

    var self = this;

    this.isPlaying = false;

    this.stopToken = 0;

 

    this.play = function () {

        self.isPlaying = true;

        self.stopToken++;

        var currentStopToken = self.stopToken;

        var overalTime = 0;

 

        function colorBlack(elements) {

            for (var i in elements) {

                var e = elements[i];

                if (e.tagName == "line" || e.tagName == "path") e.style.stroke = "#000";

                else e.style.fill = "#000";

            }

        }

        function colorRed(elements) {

            for (var i in elements) {

                var e = elements[i];

                if (e.tagName == "line" || e.tagName == "path") e.style.stroke = "#c34853";

                else e.style.fill = "#c34853";

            }

        }

 

        var notes = [];

 

 

        $("svg").children().each(function (i, e) {

 

            var delayTime = $(e).attr("data-playback-start");

            if (delayTime == null) return;

            delayTime = parseInt(delayTime);

 

            var pitch = parseInt($(e).attr("data-midi-pitch"));

            var duration = parseInt($(e).attr("data-playback-duration"));

 

            if (notes.length > 0 && notes[notes.length - 1].id == $(e).attr("id")) {

                notes[notes.length - 1].elements.push(e);

            }

            else {

                var note = { delayTime: delayTime, pitch: pitch, duration: duration, elements: [], id: $(e).attr("id") };

                note.elements.push(e);

                notes.push(note);

            }

            overalTime = delayTime + duration;

        });

 

        for (var i in notes) {

            var noteInfo = notes[i];

 

            setTimeout(function (note) {

                return function () {

                    if (self.stopToken != currentStopToken) {

                        colorBlack(note.elements);

                        return;

                    }

 

                    MIDI.noteOn(0, note.pitch, 127, 0);

                    MIDI.noteOff(0, note.pitch, note.duration * 0.001);

                    colorRed(note.elements);

                };

            }(noteInfo), noteInfo.delayTime);

 

            setTimeout(function (note) {

                return function () {

                    colorBlack(note.elements);

                };

            }(noteInfo), noteInfo.delayTime + noteInfo.duration);

        }

 

        setTimeout(function () {

            if (self.stopToken != currentStopToken) return;

            self.isPlaying = false;

        }, overalTime);

    }

 

    this.stop = function () {

        self.stopToken++;

        self.isPlaying = false;

    }

}

And reference it in cshtml file:

@Scripts.Render("~/ViewModelsClient/ClientSongViewModel.js")

The above viewmodel works only if there is a single NoteViewer control on page. If there are more NoteViewers you have to change this line:

$("svg").children()

to:

$("#your_svg_id").children()

And place NoteViewer in div with id=”#your_svg_id”;

To initialize MIDIjs you have to paste the following code:

<script type="text/javascript">

        window.onload = function () {

            MIDI.loadPlugin({

                soundfontUrl: "/soundfonts/",

                instrument: "acoustic_grand_piano",

                onprogress: function (state, progress) {

                    console.log(state, progress);

                },

                onsuccess: function () {

                    MIDI.setVolume(0, 127);

                }

            });

        };

    </script>

To start playback you can use the following code:

window.player = new ClientSongViewModel();

window.player.play();

and assign it to a buton:

<a onclick="window.player.play()">Play</a>

 

Client-side controls

In addition to server-side approach there is also a possibility to render scores on client side in javascript. It can be achieved by CSHTML5 framework (http://cshtml5.com/). Manufaktura.Controls for CSHTML5 is currently in development and it will be available soon.