最近新发现的歌谱排版软件Lilypond

在寻找乐谱绘图库的过程中,作者发现了Lilypond这款GNU软件,它可以将歌谱脚本转化为高质量的五线谱、PS文档和MIDI乐。Lilypond使用自定义的描述语言,并支持命令行调用。文章中作者分享了如何用C#脚本调用Lilypond进行乐谱排版。

事情的起因是我在制作一款歌谱生成软件,希望找找乐谱的绘图库,居然发现完全没有现成的库,不得不承认,国内外计算机水平是差着不少的。。。因为,在百度上完全找不到这方面的内容,搜索歌谱排版,歌谱转pdf等等,一直没有结果。而在google上搜英文,东西一堆一堆的,于是我就把一个个人认为非常好的软件拿过来分享一下。

Lilypond是一款GNU软件,免费而开源,可以将一篇歌谱脚本自动排版成五线谱,ps文档和midi乐,而且渲染质量非常之高,这是下面它的效果图:歌谱效果图


而它使用的脚本则是自定义语法规则的描述类语言:

\header {
  title = "LilyPond demo"
  enteredby="Han-Wen Nienhuys"
  maintainer="hanwen@xs4all.nl"
  texidoc = "heavily mutilated Edition Peters Morgenlied by Schubert"
}
\version "2.12.0"

ignoreMelisma =	\set ignoreMelismata = ##t
ignoreMelismaOff = \unset ignoreMelismata 

#(set-global-staff-size 21)

\paper  {
				%#(set-global-staff-size (* 5.8 mm))
  indent = #(* mm 4)
  line-width = #(* mm 140)
  interscoreline = 2.\mm
  between-system-space = 15\mm
  ragged-bottom = ##t 
}


modernAccidentals = {
  \set Staff.extraNatural =  ##f
  \set Staff.autoAccidentals =  #'(Staff (same-octave . 1) (any-octave . 0))
  \set Staff.autoCautionaries =  #'()  
}

melody =    \relative c'' \repeat volta 2 \context Voice = "singer" {
  \time 6/8
  \autoBeamOff
  s1*0^\markup {  \larger { \hspace #-3.0 Lieblich, etwas geschwind } }
  R2.
  r4 r8 c4 g8 |
  \acciaccatura { f16 }  e4 c8
  <<
    \new Voice { \stemUp f8. g16 }
    { \stemDown f8.[ g16] } >> \stemNeutral a8 |
  fis4  g8 c16[ b a g] f[ e] |
  d4 f8
  \transpose a' e' \relative c'' { a16[ g fis! g] f![ d]  } |
  g4. r8 gis gis |
  a4 a16.[ b32] c8[( a]) fis8 |
  g4.~ g8-\fermata
}

firstVerse = \lyricmode {
  \set stanza = "1."
  
  Sü -- ßes Licht! Aus
  \ignoreMelisma
  gol --
  \ignoreMelismaOff

  de -- nen  Pfor -- ten brichst du __ | 
  sie -- gend durch __ die Nacht. Schö -- ner Tag, du __ bist er -- wacht. __ 
}

secondVerse = \lyricmode {
  \set stanza = "2."
  いろはに כיף та та ほへど ちり  ぬるを
  
  Жъл  дю ля זה
  
  いろ はに כיף та та ほへ ちり ぬる
  
  Жъл дю ля __
}

pianoRH =  \relative c''' \repeat volta 2\new Voice {
  #(set-accidental-style 'modern)
  \voiceOne
  g16( fis a g fis g f e d c b
  \oneVoice
  a ) | 
  <g e>8( <es fis a> <d e bes'> <c e c'>\arpeggio) r8 r | 
  r8 c'( e,) f r a |
  \once \override DynamicLineSpanner   #'padding =#3
  r8
  << { fis( g) } \\
     << { a4 } { s8\> s8\! } >>
   >>

  r8 <e c g>8[  <e c g>] |
  <d c a>4. r8 \clef bass  <d b f> <d b f> |
		\crescTextCresc
		e,16_" "\<
		g c g e g d gis b gis d gis |
		c, e a e c e a,-\f\! d fis d a d |
		b d g  d b g e16. r32\fermata
	      }

pianoLH =  \relative c'' \repeat volta 2\new Voice {
    #(set-accidental-style 'modern)
    \voiceTwo
    g16( fis a g fis g
   f e d c b
    \change Staff = down
	\oneVoice
    d,) | 
     g4.( b,8) r r
    \clef treble \grace s16 r8 <bes'>8-> <bes c>8->([ <a c>)] r <f c'> |
    \clef bass
    r8 dis( e) r c c |
    f,4.  g8[ r8 g] |
    <c, c,>4. <e e,>4. |
    a,4. <d d,>4. |
    g,8 r r g16 r16\fermata 
    }

  \book {
    \score {
      << \time 6/8
	 \new Staff \with {
	   fontSize = #-3
	   \override StaffSymbol #'staff-space = #(magstep -3)
	 } <<
	   \context Staff #(set-accidental-style 'modern)
	   \melody >>
	 \lyricsto "singer" \new Lyrics \firstVerse
	 \lyricsto "singer" \new Lyrics \secondVerse
	 \new PianoStaff << 
	   \set PianoStaff.instrumentName = \markup {
	     \bold
	     \larger\larger\larger\larger
	     \huge
	     "2."
	   }
	   \context Staff = up <<
	     \pianoRH
	     \pianoLH
	   >>
	   \context Staff = down { \clef bass \skip 1*2 }
	 >> 
       >>


      \layout {
	\context {
	  \Lyrics
	  \override VerticalAxisGroup #'minimum-Y-extent = #'(-0.85 . 2.2)
	  \override LyricText #'font-size = #-1
	}
	\context {
	  \Score
	  \override Beam #'thickness = #0.55
	  \override Beam #'auto-knee-gap = #4.0
	  \override SpacingSpanner #'spacing-increment = #1.0
	  \override Stem #'stemlet-length = #0.5
	  \override Slur #'height-limit = #1.5
	}
      }
      
  \midi {
    \context {
      \Score
      tempoWholesPerMinute = #(ly:make-moment 70 4)
      }
    }
    }
  }




中间的部分就是描述了乐谱的旋律和符号,具体的内容规则就请看帮助文档了,另外,帮助文档大概有300兆左右。。。


这款软件可以很方便的通过命令行调用,(因为是GNU的嘛),使用方法就是

lilypond 文件名 (当然还有各种可选参数表,具体就看help吧)

我用C#写了段脚本生成代码,然后用cmd命令调用了一下,有需要的就当做参考吧:

/* 
* @Author: sxf
* @Date:   2014-01-12 22:02:12
* @Last Modified by:   sxf
* @Last Modified time: 2014-01-16 12:43:15
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Diagnostics;
using System.IO;

class GenerateStaff{

	String title = "Demo";
	public void Set(String title)
	{
		this.title = title;
	}
	StreamWriter sw;
	public void Generate(String str) {
    	sw = new StreamWriter(str,false);
    	sw.WriteLine("\\header {");
    	sw.WriteLine("title = \"{0}\" ",title);
    	sw.WriteLine("}");
        
    	sw.WriteLine("\\version \"2.12.0\"");
        sw.WriteLine("ignoreMelisma =\\set ignoreMelismata = ##t");
        sw.WriteLine("ignoreMelismaOff = \\unset ignoreMelismata");
        sw.WriteLine("#(set-global-staff-size {0})",21);
    	sw.WriteLine("\\paper  {{\n%#(set-global-staff-size (* {0} mm))",5.8);
        sw.WriteLine("indent = #(* mm {0})",4);
        sw.WriteLine("line-width = #(* mm {0})",140);
        sw.WriteLine("interscoreline = {0}.\\mm",2);
        sw.WriteLine("between-system-space = {0}\\mm",15);
        sw.WriteLine("ragged-bottom = ##t ");
        sw.WriteLine("}");
        sw.WriteLine("modernAccidentals = {");
        sw.WriteLine("\\set Staff.extraNatural =  ##f");
        sw.WriteLine("\\set Staff.autoAccidentals =  #\'(Staff (same-octave . 1) (any-octave . 0))");
        sw.WriteLine("\\set Staff.autoCautionaries =  #\'() ");
        sw.WriteLine("}");
    	sw.Write("melody = ");
    	writeRelative("c\'\' ");
    	// writeClef("bass");
        writeTime(6,8);
    	// sw.Write("\\repeat volta 2 \\context Voice = \"singer\" ");
    	sw.WriteLine("{");
    	
    	// sw.WriteLine("\\autoBeamOff");
    	writeClef("bass");
        // writeTime(6,8);
        // sw.WriteLine("\\autoBeamOff");
        // sw.WriteLine("s1*0^\\markup {{  \\larger {{ \\hspace #-3.0 Lieblich, etwas geschwind }} }}");
        // sw.WriteLine("R2.");
    	writeNote(1);
    	writeNote(2);
    	for (int i = 1; i < 8; ++i)
    	{
    		writeNote(i,4);
    	}
    	for (int i = 1; i < 8; ++i)
    	{
    		writeNote(i,4);
    	}
    	Random ra = new Random();
    	for (int i = 1; i < 250; ++i)
    	{
    		writeNote(ra.Next(7)+1,8);
    	}
    	sw.WriteLine("}");
    	sw.Close();
    }
    int mi(int x)
    {
    	int y = 2;
    	for (int i =1;i<x;++i)
    	{
    		y*= 2;
    	}
    	return y;
    }
    void writeRelative(String str) {
    	sw.Write("\\relative {0}",str);
    }
    void writeClef(String str){
    	sw.WriteLine("\\clef {0} ",str);
    }
    void writeTime(int p,int q) {
    	sw.WriteLine("\\time {0}/{1}",p,q);
    }
    char[] md = {'#','c','d','e','f','g','a','b'};
    void writeNote(int p) {
    	sw.Write("{0} ",md[p]);
    }
    void writeNote(int p,int q) {
    	sw.Write("{0}{1} ",md[p],q);
    }

}

class MainClass {
    static void Main() {
    	test("sxf","hello.ly");
    }
    static void test(String str,String filename) {
    	Console.WriteLine("Hello,{0}",str);
    	GenerateStaff gs = new GenerateStaff();
    	gs.Generate(filename);
    	RenderPdf(filename);
    }


    

    static void RenderPdf(String str) {
		//定义一个ProcessStartInfo实例
		System.Diagnostics.ProcessStartInfo info = new System.Diagnostics.ProcessStartInfo();
		//设置启动进程的初始目录
		info.WorkingDirectory = System.Environment.CurrentDirectory;
		//设置启动进程的应用程序或文档名
		info.FileName = System.Environment.CurrentDirectory+"\\usr\\bin\\lilypond";
		Console.WriteLine(info.WorkingDirectory);
		//设置启动进程的参数
		info.Arguments = System.Environment.CurrentDirectory + "\\"+str;
		
        //设置不显示窗口 
        info.CreateNoWindow = true; 
		//启动由包含进程启动信息的进程资源
		try
		{
			System.Diagnostics.Process.Start(info);
		}
		catch (System.ComponentModel.Win32Exception we)
		{
			Console.WriteLine(we.Message);
			return;
		}
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值