Playing C♯
※ C# Advent Calendar 2017 の12月23日の記事。
- 前の日の記事: OverrideXml | 浮遊島
- 次の日の記事: Eto.Forms の安定したGUI操作 (GTK対策) | ごった日記
過去の C# Advent Calender の記事:
- 「C# Advent Calender 2012」 12月25日: [C#][Design Pattern][DynamicObject][dynamic] C# による Observer パターンの実装 その6 - DynamicObject を使ってオブザーバーを作る
- 「C# Advent Calendar 2013」 12月12日: [C#][.NET][Roslyn] メタプログラミング入門 - Roslyn による C# ソースコードの解析と変更
- 「C# Advent Calendar 2014」 12月23日: [C#][式木][LINQ] IQueryable な Twitter のタイムライン クラスと LINQ プロバイダー
- 「C# Advent Calendar 2015」 12月19日: [C#] 浮動小数点数型 double と誤差 ~double の内部表現~
- 「C# Advent Calendar 2016」 12月23日: [C#] Tips: interface と partial class で横断的関心事を分離
今回は、C# で C♯ を演奏してみた、と言いたいだけの記事。
C♯
プログラミング言語である C# は、通常 "C#" と表記し "c sharp" と読む。
※ "C" を進化させた "C++" を更に進化させたという意味の "C++++" が由来、というのも有名な話だ。 これは、C# の父である Anders Hejlsberg 氏から直に聞いたので間違いない。
ご存知の方が多いと思うが、"C#" の # は通常「ナンバーサイン」とか「番号記号」とか「井桁」とか呼ばれるもので、英語圏でも "number" とか "pound" とか "hash" などと発音される。 音楽記号である♯とは別の文字だ。
そして、筆者はよく音楽の演奏をするのだが、C♯ と書かれていれば、それは嬰ハ長調、または、嬰ハの音 (ハ長調のド♯) を表していることが多い。
というわけで (謎)、C# で C♯ を演奏してみたい。
C# で C♯ を鳴らす
C# で音階を演奏するため、2つの方法を試してみる。
C# で C♯ を鳴らす (Beep)
一つ目は、System.Console.Beep(int frequency, int duration) を用いる方法だ。 このメソッドは、音の周波数と長さを渡して演奏するようになっている。
C# で 音名 C♯ の音を鳴らす (Beep)
音は1オクターブ上がると周波数が2倍になる。平均率の場合、音階の中の全部の半音は同じだけ音が上がっていく。 1オクターブは半音12個分だから、半音上がるごとに周波数は「2の1/12乗」倍になる計算だ。 全音上がるごとだと「2の1/6乗」倍。
なので、C# で音名 C♯ の四分音符を演奏すると、例えば次のようになる。
using System; static class MusicalScale { public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz) // 平均律 (equal temperament) | Wikipedia // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */) => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0); } // 音符の長さ enum Duration { Whole = 1600 , // 全音符 Half = Whole / 2, // 二分音符 Quarter = Half / 2, // 四分音符 Eighth = Quarter / 2, // 八分音符 Sixteenth = Eighth / 2, // 十六分音符 } class Program { const double relativePositionOfCSharp = -4.0; // C♯(嬰ハ) は A(イ) より4音下 static void Main() { PlayCSharpNote(); Console.ReadKey(); } // 音名 C♯ の音を演奏 (Beep で) static void PlayCSharpNote() => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter); // 指定された周波数/長さの音を鳴らす static void Beep(double frequency, Duration duration) => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration); }
実行すると、C♯ が聞こえてくる。
C# で C♯ の音階を平均率で演奏する (Beep)
この要領で、平均律で C♯ の音階 (嬰ハ長調のドレミファソラシド) を演奏してみよう。
using System; using System.Collections.Generic; static class EnumerableExtentions { public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action) { foreach (var element in @this) action(element); } } static class MusicalScale { public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz) // 平均律 (equal temperament) | Wikipedia // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */) => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0); } // 音符の長さ enum Duration { Whole = 1600 , // 全音符 Half = Whole / 2, // 二分音符 Quarter = Half / 2, // 四分音符 Eighth = Quarter / 2, // 八分音符 Sixteenth = Eighth / 2, // 十六分音符 } class Program { const double relativePositionOfCSharp = -4.0; // C♯(嬰ハ) は A(イ) より4音下 static void Main() { //PlayCSharpNote(); PlayCSharpEqualTemperament(); Console.ReadKey(); } //// 音名 C♯ の音を演奏 (Beep で) //static void PlayCSharpNote() // => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter); // C♯ の音階を演奏 (平均律を Beep で) static void PlayCSharpEqualTemperament() => new[] { 0.0, 1.0, 2.0, 2.5, 3.5, 4.5, 5.5, 6.0 } // ドレミファソラシドの各音がドから何音離れているか .ForEach(note => Beep(frequency: MusicalScale.EqualTemperamentFrequency(note + relativePositionOfCSharp), duration: Duration.Quarter)); // 指定された周波数/長さの音を鳴らす static void Beep(double frequency, Duration duration) => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration); }
C# で C♯ の音階を純正率で演奏する (Beep)
純正率でもやってみよう。
純正率では各音が単純な整数比になるため、和音にしたときに平均律のようにわずかな周波数のずれからくるうなりが生ずることがなく、綺麗にハモれる。
詳細は省略するが、純正率では、ドレミファソラシドの周波数が、それぞれ、1/1、9/8、5/4、4/3、3/2、5/3、15/8、2/1 となる。
これを踏まえて、C# で書いてみる。
using System; using System.Collections.Generic; using System.Linq; static class EnumerableExtentions { public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action) { foreach (var element in @this) action(element); } } static class MusicalScale { public static double A4Frequency { get; set; } = 440.0; // ベースとなる音 A4 (デフォルトは440Hz) // 純正律の場合の各音 (ドレミファソラシド) の相対的な周波数の倍率 readonly static double[] JustIntonationRelativeFrequencyScales = new[] { 1.0 / 1.0, 9.0 / 8.0, 5.0 / 4.0, 4.0 / 3.0, 3.0 / 2.0, 5.0 / 3.0, 15.0 / 8.0, 2.0 / 1.0 }; // 平均律 (equal temperament) | Wikipedia // https://ja.wikipedia.org/wiki/%E5%B9%B3%E5%9D%87%E5%BE%8B // 1オクターブ (6音) で周波数が2倍になり、半音ごとに周波数は「2の1/12乗」倍になる public static double EqualTemperamentFrequency(double relativePositionInScale /* ベースとなる音 A4 から何音上か */) => A4Frequency * System.Math.Pow(2.0, relativePositionInScale * 2.0 / 12.0); // 純正律 (just intonation) - Wikipedia // https://ja.wikipedia.org/wiki/%E7%B4%94%E6%AD%A3%E5%BE%8B public static double JustIntonationFrequency(double keynoteRelativePositionInScale, int noteIndex /* ドレミファソラシドの各音のインデックス (0~7) */) => EqualTemperamentFrequency(keynoteRelativePositionInScale) * JustIntonationRelativeFrequencyScales[noteIndex]; } // 音符の長さ enum Duration { Whole = 1600 , // 全音符 Half = Whole / 2, // 二分音符 Quarter = Half / 2, // 四分音符 Eighth = Quarter / 2, // 八分音符 Sixteenth = Eighth / 2, // 十六分音符 } class Program { const double relativePositionOfCSharp = -4.0; // C♯(嬰ハ) は A(イ) より4音下 static void Main() { //PlayCSharpNote(); //PlayCSharpEqualTemperament(); PlayCSharpJustIntonation(); Console.ReadKey(); } //// 音名 C♯ の音を演奏 (Beep で) //static void PlayCSharpNote() // => Beep(frequency: MusicalScale.EqualTemperamentFrequency(relativePositionOfCSharp), duration: Duration.Quarter); //// C♯ の音階を演奏 (平均律を Beep で) //static void PlayCSharpEqualTemperament() // => new[] { 0.0, 1.0, 2.0, 2.5, 3.5, 4.5, 5.5, 6.0 } // ドレミファソラシドの各音がドから何音離れているか // .ForEach(note => Beep(frequency: MusicalScale.EqualTemperamentFrequency(note + relativePositionOfCSharp), duration: Duration.Quarter)); // C♯ の音階を演奏 (純正率を Beep で) static void PlayCSharpJustIntonation() => Enumerable.Range(start: 0, count: 8) // ドレミファソラシドの各音のインデックス (0~7) .ForEach(noteIndex => Beep(frequency: MusicalScale.JustIntonationFrequency(relativePositionOfCSharp, noteIndex), duration: Duration.Quarter)); // 指定された周波数/長さの音を鳴らす static void Beep(double frequency, Duration duration) => Console.Beep(frequency: (int)System.Math.Round(frequency), duration: (int)duration); }
C# で C♯ を鳴らす (Microsoft.SmallBasic.Library.Sound.PlayMusic)
もう一つ試そう。 Small Basic の機能を使う方法だ。
そのために、まず Visual Studio から NuGet で "SmallBasicLib" をインストールする。
このライブラリーの機能の一つに、MML (Music Macro Language) を演奏するというものがある。 Microsoft.SmallBasic.Library.Sound.PlayMusic というメソッドを使うことでテキストで記述された旋律を演奏できる。
これを使って、平均律で C♯ の音階 (ドレミファソラシド) を演奏してみよう。 詳しい MML の記述方法は、下記ソースコードのコメントを見てほしい。
C# で C♯ の音階を平均律で演奏する (Microsoft.SmallBasic.Library.Sound.PlayMusic)
using Microsoft.SmallBasic.Library; using System; class Program { static void Main() { PlayCSharpMusic(); Console.ReadKey(); } // C♯ の音階を演奏 (平均率を Small Basic Library で) static void PlayCSharpMusic() => // C♯ (嬰ハ長調) | Wikipedia // https://ja.wikipedia.org/wiki/%E5%AC%B0%E3%83%8F%E9%95%B7%E8%AA%BF // シャープ7箇所(F, C, G, D, A, E, B) // C♯ D♯ F F♯ G♯ A♯ C C♯ (嬰ハ 嬰二 ヘ 嬰ヘ 嬰ト 嬰イ ハ 嬰ハ) Sound.PlayMusic(notes: "L4 O4 C+ D+ F F+ G+ A+ > C C+"); // Sound.PlayMusic の notes // // C, D, E, F, G, A, B: 音名 // R : 休符 // C+: Cは音名、音を半音上げる (♯) // C-: Cは音名、音を半音下げる (♭) // Cn: n は音の長さ (1: 全音符、2: 二分音符、2.: 付点二分音符、4: 四分音符、4.: 付点四分音符、…) // Ln: n はデフォルトの音の長さ // On: n はオクターブ (デフォルトは4) // > : 1オクターブ上に // < : 1オクターブ下に }
C# で C♯ の曲を演奏する (Microsoft.SmallBasic.Library.Sound.PlayMusic)
実は C♯、つまり嬰ハ長調の曲というのはそんなに多くないのだが、例えば、バッハ の「平均律クラヴィーア 第1巻 プレリュード 第3番 嬰ハ長調 BWV 848」や「前奏曲とフーガ 第3番 嬰ハ長調 BWV 872」などがそうだ。
例えば下記で聴くことができる (執筆時点)。
試しに、一部だけ C# で書いてみよう。 C♯ の響きを雰囲気だけでも味わっていただければ幸いだ。
using Microsoft.SmallBasic.Library; using System; class Program { static void Main() { PlayBWV848(); //PlayCSharpMusic(); Console.ReadKey(); } // バッハ 平均律クラヴィーア 第1巻 プレリュード 第3番 嬰ハ長調 BWV 848 // 雰囲気だけ static void PlayBWV848() => Sound.PlayMusic("L16 O5 E+ C+ > G+ < C+ E+ C+" + "F+ C+ F+ C+ F+ C+" + "G+ C+ G+ C+ G+ C+" + "A+ C+ A+ C+ A+ C+" + "G+ C+ G+ C+ G+ C+" + "F+ E+ D+ E+ F+ D+" + "E+ D+ C+ D+ E+ C+" + "D+ E+ D+ C+ < B+ A+" + "< B+ G+ D+ G+ B+ G+" + "> C+ < G+ > C+ < G+ > C+ < G+" + "> D+ < G+ > D+ < G+ > D+ < G+" + "> E+ < G+ > E+ < G+ > E+ < G+" + "> D+ < G+ > D+ < G+ > D+ < G+" + "> C+ < B+ A+ B+ > C+ < A+" + "B+ A+ G+ A+ B+ G+" + "A+ B > > F+ E+ D+ E+" + "F+ D+ < A+ > D+ F+ D+" + "G+ D+ G+ D+ G+ D+" + "A+ D+ A+ D+ A+ D+" + "B D+ B D+ B D+" + "A+ D+ A+ D+ A+ D+" + "G+ F+ E+ F+ G+ E+" + "F+ E+ D+ E+ F+ D+" + "E+ F+ E+ D+ C+ < B+"); //// C♯ の音階を演奏 (平均率を Small Basic Library で) //static void PlayCSharpMusic() => // // C♯ (嬰ハ長調) | Wikipedia // // https://ja.wikipedia.org/wiki/%E5%AC%B0%E3%83%8F%E9%95%B7%E8%AA%BF // // シャープ7箇所(F, C, G, D, A, E, B) // // C♯ D♯ F F♯ G♯ A♯ C C♯ (嬰ハ 嬰二 ヘ 嬰ヘ 嬰ト 嬰イ ハ 嬰ハ) // Sound.PlayMusic(notes: "L4 O4 C+ D+ F F+ G+ A+ > C C+"); // Sound.PlayMusic の notes // // C, D, E, F, G, A, B: 音名 // R : 休符 // C+: Cは音名、音を半音上げる (♯) // C-: Cは音名、音を半音下げる (♭) // Cn: n は音の長さ (1: 全音符、2: 二分音符、2.: 付点二分音符、4: 四分音符、4.: 付点四分音符、…) // Ln: n はデフォルトの音の長さ // On: n はオクターブ (デフォルトは4) // > : 1オクターブ上に // < : 1オクターブ下に }