Midiキーボードを使用してLilyPondを入力します

私はすでにリリーポンドについて何度 書いていますが、今ではミディキーボードを買いました。



FinaleやSibeliusを含む多くのノートエディターには、2つの方法でMIDIキーボードからノートを入力する機能があります:メトロノームで何かを演奏し、すぐにノートを録音するか、ノートからのみ入力でき、リズムと他のすべては通常の方法で入力されます。



私は同様の機会が私の好みのlilypond'aに害を与えないと判断しました。 Midiファイルを書き込んでからmidi2lyを使用して変換する機能は私には合わないので、入力タイプだけの情報をmidiファイルに反映することはできません( これについては既に説明しました )-を押すようにプログラムを書くことにしましたキーとコードはすぐに必要な形式に変換されます。



UPD:以下の約半分に注意書きが必要です



\ relativeコマンドを使用してlilypondに入力すると、各音にオクターブを指定するのではなく、オクターブが変化する方向のみを示すことができます。 そのような表示がない場合、後続の各音(和音の場合、最初に表示される音)は4分の1以下です。 つまり、fがシャープで、かつbeforeがフラット(2倍、クォート、はい)であっても、 beforeの後のfaのスコアが高くなります。



プログラムを書くことで、実際にMIDIキーボードで作業することと、受信した信号を希望の形式に変換することの2種類の困難に直面しました。



midi-dot-net:MIDIでの作業



私が最初にしたことは、 midi-dot-netライブラリをダウンロードすることでした。 入力と出力の両方の可能性を提供しますが、現在は入力に興味があります。



デバイスのリスト、開閉


利用可能な入力デバイスのリスト、私はそれをコンボボックスに詰めました。



using Midi; // ....... private void LoadMidiDevices() { foreach (InputDevice d in InputDevice.InstalledDevices) { DeviceList.Items.Add(d.Name); } DeviceList.SelectedIndex = 0; UpdateDevice(); }
      
      







デバイス名に加えて、ManufacturerId、ProductId、およびこれらすべてを1つのフィールドSpecで確認することもできます。



Open()およびClose()メソッドはデバイスを開いたり閉じたりします。情報は、IsOpen、StartReceiving()、StopReceiving()、IsReceivingの各フィールドから取得でき、情報の受信を担当します。 このライブラリは、便利なC#スタイルのイベントバインディングを提供します。



StartReceiving()メソッドは、オプションで、主に遅延MIDI出力用のClock型のオブジェクトを受け入れます。 nullを渡すと、イベントのタイムスタンプはStartReceiving()が呼び出された時点からカウントされます。



 private void UpdateDevice() { if (d != null) { if (d.IsReceiving) d.StopReceiving(); if (d.IsOpen) d.Close(); } d = InputDevice.InstalledDevices[DeviceList.SelectedIndex]; if (!d.IsOpen) d.Open(); if (d.IsReceiving) d.StopReceiving(); d.StartReceiving(null); if (d != null) { d.NoteOn += new InputDevice.NoteOnHandler(this.NoteOn); d.NoteOff += new InputDevice.NoteOffHandler(this.NoteOff); d.ControlChange += new InputDevice.ControlChangeHandler(this.ControlChange); } }
      
      







終了するときにデバイスを閉じることを忘れないでください

 private void SettingsWindow_FormClosing(object sender, FormClosingEventArgs e) { d.StopReceiving(); d.Close(); }
      
      







処理をクリックします


このように操作アルゴリズムを選択しました。すべてのキーを離すと、50ミリ秒以上押されたキーが一括してコンバーターに転送され、SendKeysを使用してアクティブウィンドウに送信されます。 これにより、個々の音符と和音の両方をダイヤルできます。



実装は不名誉に簡単です:

NoteOn // NoteOff
 private List<Pitch> notes; private Dictionary<Pitch, float> events; //.... public void NoteOn(NoteOnMessage msg) { lock (this) { events[msg.Pitch] = msg.Time; } } public void NoteOff(NoteOffMessage msg) { lock (this) { if (events.ContainsKey(msg.Pitch)) { if (msg.Time - events[msg.Pitch] > 0.05) { notes.Add(msg.Pitch); } events.Remove(msg.Pitch); if ((events.Count == 0) && (notes.Count > 0)) { SendKeys.SendWait(" " + cons.Convert(notes)); notes.Clear(); } } } }
      
      







また、サスティーンペダル(および私の場合はボタン)を使用して、リーグ(lilypond構文のかっこ)を設定する機能も追加しました。 キーボードはControlChangeを2つに分けて送信するため、追加のチェックを追加しました。



 bool sustain; // ....... public void ControlChange(ControlChangeMessage msg) { if (msg.Control == Midi.Control.SustainPedal) { if ((msg.Value > 64) && !sustain) { sustain = true; SendKeys.SendWait("{(}"); } if ((msg.Value < 64) && sustain) { sustain = false; SendKeys.SendWait("{)}"); } } return; }
      
      







ライブラリでは、ProgramChangeおよびPitchBendイベントを処理することもできます。



私たちは音楽の初歩的な理論の実をかみます



2番目のタスクは最初のタスクよりも困難でした。 文字数とメジャー/マイナースイッチを示すカウンターをウィンドウに追加し、考え始めました。



まず、Midi.Pitchの値と結論の中間リンクを作成しました-ガンマステップクラス(辞書を見るのが面倒で、最初はGradeと呼ばれますが、学位が必要でした)。 キーに応じて、Midi.PitchをDegreeに変換します。

配列には、色(12ステップ)スケールの数値を7ステップスケールのステップとその変更(増加または減少)に変換するための設定が保存されます。

PitchToGrade()
 private static int[] majorScale = { 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6 }; private static int[] majorAcc = { 0, -1, 0, -1, 0, 0, 1, 0, -1, 0, -1, 0 }; private static int[] minorScale = { 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6 }; private static int[] minorAcc = { 0, -1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1 }; private Degree PitchToGrade(Pitch p) { //      int keybase = (keys * 7) % 12 - (isMajor?0:3); //          int offset = ((int)p - keybase) % 12; //     7-  int octave = (((int)p - keybase) / 12) * 7; int num, acc; if (offset < 0) offset += 12; if (isMajor) { num = majorScale[offset] + octave; acc = majorAcc[offset]; } else { num = minorScale[offset] + octave; acc = minorAcc[offset]; } return new Degree(num, acc); }
      
      







その後、最も不愉快なことが始まります。どの音符とどの記号でこの番号と与えられた調性のこの変更を伴うステップを示すかを調べることです。



Degreeクラスのメソッドはこのように機能します。 fs変数には、キー最低限必要な文字数が含まれています 。これは、変更されていない形式のこのキーのこのステップに変更記号があるようにするために必要です。 変数Numberには、オクターブnumModを考慮して-考慮せずにステップの番号が格納されます。



魔法のように(キー文字の数の4倍を加算し、マイナーの場合は2を減算します)、ステップ番号は白鍵スケールのノート番号に変わります。fs変数が必要を示す場合、「ネイティブ」の調性変更を追加します。

resolveIn()
 private static String[] Naturals = { "c", "d", "e", "f", "g", "a", "h" }; private static String[] Sharps = { "cis", "dis", "eis", "fis", "gis", "ais", "his" }; private static String[] Flats = { "ces", "des", "es", "fes", "ges", "as", "b" }; private static String[] DoubleSharps = { "cisis", "disis", "eisis", "fisis", "gisis", "aisis", "bisis" }; private static String[] DoubleFlats = { "ceses", "deses", "eses", "feses", "geses", "ases", "beses" }; public String resolveIn(int keys, bool isMajor) { int fAcc = Acc; int fs; int fNum; int numMod = Number % 7; fs = isMajor ? 6 : 3; fs = (fs - 2*numMod) % 7; if (fs <= 0) fs += 7; if (keys < 0) fs = 8 - fs; if (fs <= Math.Abs(keys)) fAcc += keys / Math.Abs(keys); fNum = (numMod + keys*4 - (isMajor ? 0 : 2)) % 7; if (fNum < 0) fNum += 7; switch (fAcc) { case -2: return DoubleFlats[fNum]; case -1: return Flats[fNum]; case 0: return Naturals[fNum]; case 1: return Sharps[fNum]; case 2: return DoubleSharps[fNum]; default: return ""; } }
      
      







残りは簡単です。調性を変更する兆候を追加する必要性は、ステップ減算演算子によって計算され、最後の高さはlastPitchフィールドに格納されます。
変換()およびその他すべて
 // class Degree public static String operator -(Degree a, Degree g) { int o; o = a.Number - g.Number; o = (int)Math.Round((double)o / 7.0); if (o > 0) return new String('\'', o); if (o < 0) return new String(',', -o); return ""; } // class PitchConsumer public String Convert(List<Pitch> pitches) { Pitch localLast = lastPitch; String accum; if ((int)lastPitch == 0) lastPitch = pitches[0]; pitches.Sort(); if (pitches.Count == 1) { accum = PitchToString(pitches[0], lastPitch); lastPitch = pitches[0]; } else { lastPitch = pitches[0]; accum = "<"; foreach (Pitch p in pitches) { if (accum.Length > 1) accum += " "; accum += PitchToString(p, localLast); localLast = p; } accum += ">"; } return accum; } private String PitchToString(Pitch p, Pitch last) { Degree g, glast; String note; g = PitchToGrade(p); glast = PitchToGrade(last); note = g.resolveIn(keys, isMajor); return note + (g - glast); }
      
      









結論



複雑さは、私が確信していなかった地域ではなく、私の専門の主題である私を待っていました。 しかし今、私は音楽学校の子供たちにとってそれがどれほど難しいかをよく理解しています:-)。 そして、メモの取得が本当に速くなりました。



https://github.com/m0003r/LilyInput



All Articles