zoukankan      html  css  js  c++  java
  • .NET中的音乐符号

    介绍 本文简要介绍了库Manufaktura最重要的基础知识。控件,我最近作为开源项目发布了。这个项目是我八年前创建的另外两个项目的延续,在以下文章中描述: https://www.codeproject.com/articles/87329/psam-control-library https://www.codeproject.com/articles/89582/psam-wpf-control-library|0>>>> 这些年来,我的编程技能有了显著的提高,但Manufaktura。控件仍然使用来自这两个旧项目的一些代码。我完全改变了架构以允许跨平台实现,但你仍然可以找到一些旧的意大利面条代码,特别是在呈现策略的主体部分。 我将代码作为开源项目发布,因为作为一个纯粹的商业项目,它没有给我带来太多利润。我希望它作为开源软件会更有用,一个大型社区将很快围绕它出现。 由于附件大小的限制,本文附带的源代码只包含最重要的库。您可以在GIT存储库中找到整个代码:https://bitbucket.org/Ajcek/manufakturalibraries 它的作用是什么(针对那些不熟悉我的库的人) Manufaktura。控件是一组。net库,用c#编写,用于在桌面、web和移动应用程序中渲染音乐符号。核心库是跨平台的,所以它们可以在几乎任何。net环境中使用,比如。net Framework、。net core、UWP、Mono、Silverlight等等。有WPF, WinForms, UWP, ASP的实现。净MVC, ASP。核心网等。还有针对Silverlight和Windows 8的遗留库。 这些库的主要目的是呈现乐谱,但它们也提供了其他特性,如MusicXML解析、MIDI回放和用于数学操作的帮助方法,这些数学操作在音乐中很有用。 解决方案概述 解决方案中有两个主要的库:Manufaktura。控制和Manufaktura.Music。这些都是跨平台的库,因此它们可以在所有。net环境中使用:web、桌面和移动环境。以前,它们作为可移植的类库,目前它们的目标是。net标准1.1。 Manufaktura。音乐定义了音乐的低级概念,如音程、节奏、比例等。它独立于音乐符号所以模型是由Manufaktura定义的。音乐是不知道音符、休止符、谱号等概念的。这个库还定义了模型之间的算术运算,比如音程的调换、音高的比较等等。 Manufaktura。控件是解决方案中最大的库。定义了西方音乐符号模型、渲染器、解析器等。 建筑Manufaktura.Music Manufaktura的主要概念。音乐包括:音程、音高、步幅、比例、有节奏的时长和音阶。 音高概念用三种结构来表示: Step -定义一个可以改变(增加或减少)的尺度步长(a, B, C, D, E, F,或G)。步骤不知道确切的音高。Pitch -从Step继承。这是一个精确的八度音和精确的中音。TunedPitch -从Pitch继承。这是一个有着精确频率的音高。例如:A4在440Hz或A4在415Hz。 间隔也分为三种结构: 间隔时间-由步数定义。例如:二度、三度、八度等等。Interval -继承自DiatonicInterval。定义步数和半步数。例如:小调二度、大调二度、减三度等等。BoundInterval—从interval继承。表示从特定的音高概念开始的间隔,类似于钩状向量。例:从C到E的三大调。 比例只是分数,可以转换成两倍或分。例如:比例。Sesquialtera返回3/2的分数。 还有其他种类,如节奏和音阶。所有这些类或多或少都用于Manufaktura.Controls的低抽象模型中。 建筑Manufaktura.Controls Manufaktura。控件是解决方案中最大的库。它定义了模型、解析器(用于解析MusicXml)、呈现器和呈现策略。 模型 概述 模型包含代表不同西方音乐符号概念的类,如音符、休止符、谱号、线条等。在很大程度上,模型是基于MusicXml规范的,但它也利用了来自Manufaktura的概念。例如,音乐库中的音符由音高和节奏持续时间组成。一些类来自manufakura . controls。模型可以看作是来自Manufaktura的较低层次的抽象。比如音乐类,音高可以提升为音符,音符可以降低为音高,等等。 创建分数模型 乐谱包含所有要绘制的音乐符号。创建分数有两种方法: 手动使用api自动解析器 您可以学习基本的手工创建分数模型从这篇文章: http://musicengravingcontrols.com/en-我们/文章/显示? id = 2 员工的规则 手动创建模型与使用解析器创建分数不同。当您解析MusicXml时,解析器会自动应用MusicXml文件中包含的一些数据,比如度量中的水平音符位置、词干方向等。 如果您通过API手动添加注释和其他符号,一些属性(如词干方向)是由人员规则决定的。人员规则是从StaffRule继承的类。例如,当向五线谱插入注释时,NoteStemRule会自动确定词干的方向。当将StaffRules插入到继承自itemmanagingcollectiontitem>的集合时,将自动应用这些规则。这个类最常用的实现是管理Staff上的项的MusicalSymbolCollection。 渲染器和渲染策略 Manufaktura。控件为每个平台使用一个代码基。这是通过称为渲染器和渲染策略的抽象类实现的: 渲染器-他们定义如何绘制原始形状,如线,文本和贝塞尔曲线,但他们完全不知道音乐符号。渲染策略——将音乐转换成原始的形状,如线条、文本和贝塞尔曲线,但不知道如何绘制它们。 ScoreRendererBase类有五个主要的抽象方法: 导入 这五个方法是在派生类中实现的。例如,WPFCanvasScoreRenderer通过创建线条形状并将其放置在画布上来绘制线条: 隐藏,复制Code

    public override void DrawLine(Primitives.Point startPoint, 
            Primitives.Point endPoint, Primitives.Pen pen, MusicalSymbol owner)
    {
        if (!EnsureProperPage(owner)) return;
        if (Settings.RenderingMode != ScoreRenderingModes.Panorama)
        {
            startPoint = startPoint.Translate(CurrentScore.DefaultPageSettings);
            endPoint = endPoint.Translate(CurrentScore.DefaultPageSettings);
        }
    
        var line = new Line();
        line.Stroke = new SolidColorBrush(ConvertColor(pen.Color));
        line.X1 = startPoint.X;
        line.X2 = endPoint.X;
        line.Y1 = startPoint.Y;
        line.Y2 = endPoint.Y;
        line.StrokeThickness = pen.Thickness;
        line.Visibility = BoolToVisibility(owner.IsVisible);
        Canvas.Children.Add(line);
        OwnershipDictionary.Add(line, owner);
    }

    HtmlSvgScoreRenderer创建SVG标签并将其添加到表示SVG画布的XML文档中: 隐藏,收缩,复制Code

    public override void DrawLine(Point startPoint, Point endPoint, Pen pen, Model.MusicalSymbol owner)
            {
                if (!EnsureProperPage(owner)) return;
                if (Settings.RenderingMode != ScoreRenderingModes.Panorama && 
                                                  !TypedSettings.IgnorePageMargins)
                {
                    startPoint = startPoint.Translate(CurrentScore.DefaultPageSettings);
                    endPoint = endPoint.Translate(CurrentScore.DefaultPageSettings);
                }
    
                var element = new XElement("line",
                    new XAttribute("x1", startPoint.X.ToStringInvariant()),
                    new XAttribute("y1", startPoint.Y.ToStringInvariant()),
                    new XAttribute("x2", endPoint.X.ToStringInvariant()),
                    new XAttribute("y2", endPoint.Y.ToStringInvariant()),
                    new XAttribute("style", pen.ToCss()),
                    new XAttribute("id", BuildElementId(owner)));
    
                var playbackAttributes = BuildPlaybackAttributes(owner);
                foreach (var playbackAttr in playbackAttributes)
                {
                    element.Add(new XAttribute(playbackAttr.Key, playbackAttr.Value));
                }
    
                if (startPoint.Y < ClippedAreaY) ClippedAreaY = startPoint.Y;
                if (endPoint.Y < ClippedAreaY) ClippedAreaY = endPoint.Y;
                if (startPoint.X > ActualWidth) ActualWidth = startPoint.X;
                if (endPoint.X > ActualWidth) ActualWidth = endPoint.X;
                if (startPoint.Y > ActualHeight) ActualHeight = startPoint.Y;
                if (endPoint.Y > ActualHeight) ActualHeight = endPoint.Y;
    
                Canvas.Add(element);
            }

    注意,Canvas属性可以是任何对象(例如,控件、XML文档等)。画布类型作为ScoreRenderer的类型参数提供。 渲染策略来自于MusicalSymbolRenderStrategy。这是一个泛型类型——适当的呈现策略由类型参数匹配。例如,诺特恩的策略来自于音乐符号。每个渲染器的主要方法是: 隐藏,复制Code

    public override void Render(Barline element, ScoreRendererBase renderer)

    第一个参数是一个要绘制的元素。第二个参数是分数渲染器实例。每个平台使用不同的渲染器实现,但是渲染方法的代码独立于任何实现—它只是告诉渲染器绘制原始形状,如线、文本等,其余的由渲染器完成。 在呈现分数时,会为分数的每个元素注入不同的呈现策略,这样每个元素都会使用适当的呈现策略呈现。注意,不同的渲染器策略的构造函数采用不同的参数,例如,KeyRenderStrategy只使用IScoreService,而notenderstrategy也使用IBeamingService, IMeasurementService等。这些服务通过简单的IoC机制自动注入到每个呈现策略中。这些服务的作用是在不同的呈现策略实例之间存储共享数据,并为一些可以在不同呈现器之间重用的更复杂的计算提供帮助。最常用的服务是IScoreService,其中存储了员工的当前X职位。 这是绘制时间签名的渲染策略的一个例子: 隐藏,收缩,复制Code

    /// <summary>
    /// Strategy for rendering a TimeSignature.
    /// </summary>
    public class TimeSignatureRenderStrategy : MusicalSymbolRenderStrategy<TimeSignature>
    {
        /// <summary>
        /// Initializes a new instance of TimeSignatureRenderStrategy
        /// </summary>
        /// <paramname="scoreService"></param>
        public TimeSignatureRenderStrategy(IScoreService scoreService) : base(scoreService)
        {
        }
    
        /// <summary>
        /// Renders time signature symbol with specific score renderer
        /// </summary>
        /// <paramname="element"></param>
        /// <paramname="renderer"></param>
        public override void Render(TimeSignature element, ScoreRendererBase renderer)
        {
            var topLinePosition = scoreService.CurrentLinePositions[0];
            if (element.Measure.Elements.FirstOrDefault() == element)
                scoreService.CursorPositionX += renderer.LinespacesToPixels(1); //Żeby był lekki 
                         //margines między kreską taktową a symbolem. 
                         //Być może ta linijka będzie do usunięcia
    
            if (element.SignatureType != TimeSignatureType.Numbers)
            {
                renderer.DrawCharacter(element.GetCharacter
                            (renderer.Settings.CurrentFont), MusicFontStyles.MusicFont,
                    scoreService.CursorPositionX, topLinePosition + 
                            renderer.LinespacesToPixels(2), element);
                element.TextBlockLocation = new Primitives.Point
                (scoreService.CursorPositionX, topLinePosition + renderer.LinespacesToPixels(2));
            }
            else
            {
                if (renderer.IsSMuFLFont)
                {
                    renderer.DrawString(SMuFLGlyphs.Instance.BuildTimeSignatureNumberFromGlyphs
                                       (element.NumberOfBeats),
                        MusicFontStyles.MusicFont, scoreService.CursorPositionX, 
                        topLinePosition + renderer.LinespacesToPixels(1), element);
                    renderer.DrawString(SMuFLGlyphs.Instance.BuildTimeSignatureNumberFromGlyphs
                                       (element.TypeOfBeats),
                        MusicFontStyles.MusicFont, scoreService.CursorPositionX, 
                        topLinePosition + renderer.LinespacesToPixels(3), element);
    
                    element.TextBlockLocation = new Primitives.Point
                    (scoreService.CursorPositionX, topLinePosition + renderer.LinespacesToPixels(3));
                }
                else
                {
                    renderer.DrawString(Convert.ToString(element.NumberOfBeats),
                        MusicFontStyles.TimeSignatureFont, scoreService.CursorPositionX, 
                        topLinePosition + renderer.LinespacesToPixels(2), element);
                    renderer.DrawString(Convert.ToString(element.TypeOfBeats),
                        MusicFontStyles.TimeSignatureFont, scoreService.CursorPositionX, 
                        topLinePosition + renderer.LinespacesToPixels(4), element);
    
                    element.TextBlockLocation = new Primitives.Point(scoreService.CursorPositionX, 
                    topLinePosition + renderer.LinespacesToPixels(4));
                }
            }
            scoreService.CursorPositionX += 20;
        }
    }

    特定于平台的实现 概述 解决方案中数量最多的库是manufakura . controls的特定于平台的实现。就类的数量而言,这些库也是最小的。通常,它们包含以下项目: 针对特定平台控件(桌面和移动)或Razor扩展(web)的ScoreRenderer实现,利用特定的ScoreRenderer来绘制分数,有时还提供一些用户体验逻辑,如注释拖拽等。媒体播放器-用于分数回放。WPF和WinForms版本使用了一个共享的MIDI playbach实现,包含在manufakura . control . desktop中。 字体和字体度量 Manufaktura。控件使用两种字体: Polihymnia -由Ben Laenen创建的字体Euterpe (https://packages.debian.org/stable/fonts/fonts-oflb-euterpe)。这种字体只有来自原始Euterpe字体的字符子集,所以你应该避免使用它。SMuFL-compliant字体。您可以使用任何与SMuFL 1.2标准兼容的字体。本文解释了如何加载SMuFL字体和元数据:http://manufaktura-programow.pl/en-US/Articles/Display?id=8 如果你打算实现你自己的ScoreRenderer,有一些关于字体度量的事情你应该知道。首先,Polihymnia和SMuFL字体的设计方式是基线的位置与标尺(或场中心)上的线的位置重合。例如,如果您在第3行的Y坐标处放置一个notehead,那么notehead的中心将准确地显示在第3行上。不幸的是,特定的框架提供了两种完全不同的te方式xt定位: 文本块坐标是字体基线的坐标。HTML SVG就是这样工作的。文本块坐标是文本块边框左下角的坐标(例如:WPF中的文本块元素)。 第二个行为是不需要的,所以我们应该通过按基线位置转换文本块的位置来纠正文本位置。基线坐标可以从字体度量中读取,对于每个平台,字体度量必须以不同的方式检索。例如,WPF是这样做的: 隐藏,复制Code

    var baseline = typeface.FontFamily.Baseline * textBlock.FontSize;
    Canvas.SetLeft(textBlock, location.X);
    Canvas.SetTop(textBlock, location.Y - baseline);

    这在WinForms (System.Graphics)中是完全不同的: 隐藏,复制Code

    var baselineDesignUnits = font.FontFamily.GetCellAscent(font.Style);
    var baselinePixels = (baselineDesignUnits * font.Size) / font.FontFamily.GetEmHeight(font.Style);
    Canvas.DrawString(text, font, new SolidBrush(ConvertColor(color)), 
                      new PointF((float)location.X - 4, (float)location.Y - baselinePixels));

    在UWP应用程序中没有简单的方法来做到这一点,所以我决定让程序员手动提供基线位置。 测试应用程序和单元测试 在解决方案中,有一些测试应用程序和单元测试项目。最重要的是手工操作、控制、视觉检测。这是一个单元测试项目,它将一些预定义的分数(以MusicXml格式提供)呈现为位图。它将创建的位图与之前创建的位图进行比较,并将所有差异标记为红色,以便您可以轻松地跟踪提交之间的回归情况。 这张照片显示了样例视觉测试结果。在两个版本的代码之间做了一些slur更正。差异用红色表示: 额外的资源 你可以阅读其他关于Manufaktura的文章和教程。控制: http://musicengravingcontrols.com/en-US/Articles/Display?id=2 这是一个Bitbucket库: https://bitbucket.org/Ajcek/manufakturalibraries/src 两篇关于开始一切的老项目的文章: https://www.codeproject.com/articles/87329/psam-control-library https://www.codeproject.com/articles/89582/psam-wpf-control-library|0>>>> 仍然需要改进的领域 适当的Xamarin的实现 有一个Xamarin。表单实现与渲染器的Android。它仍然处于测试状态,我没有时间去开发它。 渲染管道 呈现机制没有针对呈现许多页面进行优化。它首先呈现所有的音符和音乐符号,然后绘制所有的线。还应该进行某种虚拟化,以便只呈现屏幕上可见的分数部分。 支持更多的音乐符号概念 一些音乐符号仍然不支持。例如,库不能渲染渐强和渐弱标记。 HTML中的客户端呈现 目前,所有的web实现(manufakura . controls)。AspNetMvc, manufakura . controls . aspnetcore)使用服务器端呈现。 我尝试使用CSHTML5 (http://cshtml5.com/)实现这个功能,但是我没有时间处理它,而且CSHTML5仍在开发中。有三种可能实现客户端呈现: 继续执行CSHTML5项目。可以使用一些基于WebAssembly的库,比如Blazor。创建一个创建Vexflow (http://www.vexflow.com/)或Verovio (http://www.verovio.org/index.xhtml)脚本的ScoreRenderer。 本文转载于:http://www.diyabc.com/frontweb/news17315.html

  • 相关阅读:
    Git的使用
    Ninject.Extensions.
    centos6的安装
    ASP.NET 5+EntityFramework 7
    Nancy和MVC的简单对比
    ASP.NET 5应用是如何运行的(3)
    映射层超类型
    缓存模块
    怎样的中奖算法能让人信服(转)
    JQuery Easy Ui (Tree树)详解(转)
  • 原文地址:https://www.cnblogs.com/Dincat/p/13494051.html
Copyright © 2011-2022 走看看