C#演奏《山楂樹》

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Console_Sirius
{
class Program
{
//根據(jù)API引入外部函數(shù),MIDI的三個(gè)主要函數(shù),打開,操作,關(guān)閉
[DllImport("winmm.dll")]
private static extern int midiOutOpen(out int sirius_MidiOut, int uDeviceID, int dwCallback, int dwInstance, int dwFlags);
[DllImport("winmm.dll")]
private static extern int midiOutShortMsg(int hMidiOut, int dwMsg);
[DllImport("winmm.dll")]
private static extern int midiOutClose(int hMidiOut);
enum Scale//共有128個(gè)MIDI國際標(biāo)準(zhǔn)音高0-127,我們以大鋼琴的88個(gè)音為基準(zhǔn)建立枚舉數(shù)值,中央C為小字一組c1,MIDI音高為60號。
{
Rest = 0,
A1 = 21, A1s = 22, B1 = 23,
C1 = 24, C1s = 25, D1 = 26, D1s = 27, E1 = 28, F1 = 29, F1s = 30, G1 = 31, G1s = 32, A = 33, As = 34, B = 35,
C = 36, Cs = 37, D = 38, Ds = 39, E = 40, F = 41, Fs = 42, G = 43, Gs = 44, a = 45, aS = 46, b = 47,
c = 48, cs = 49, d = 50, ds = 51, e = 52, f = 53, fs = 54, g = 55, gs = 56, a1 = 57, a1s = 58, b1 = 59,
c1 = 60, c1s = 61, d1 = 62, d1s = 63, e1 = 64, f1 = 65, f1s = 66, g1 = 67, g1s = 68, a2 = 69, a2s = 70, b2 = 71,//小字一組,c1為中央C,60號音高
c2 = 72, c2s = 73, d2 = 74, d2s = 75, e2 = 76, f2 = 77, f2s = 78, g2 = 79, g2s = 80, a3 = 81, a3s = 82, b3 = 83,
c3 = 84, c3s = 85, d3 = 86, d3s = 87, e3 = 88, f3 = 89, f3s = 90, g3 = 91, g3s = 92, a4 = 93, a4s = 94, b4 = 95,
c4 = 96, c4s = 97, d4 = 98, d4s = 99, e4 = 100, f4 = 101, f4s = 102, g4 = 103, g4s = 104, a5 = 105, a5s = 106, b5 = 107,
c5 = 108
};
enum Voice//上面的Scale更方便五線譜讀譜,由于我們這里主要的依據(jù)是簡譜,為了代碼的可讀性及其簡化,所以再從Scale中選取以小字一組(c1-b2)為中音區(qū)的五個(gè)音區(qū),X極低音,L低音,M中音,H高音,Y極高音。
{
X1 = Scale.C, X2 = Scale.D, X3 = Scale.E, X4 = Scale.F, X5 = Scale.G, X6 = Scale.a, X7 = Scale.b,//極低音
L1 = Scale.c, L2 = Scale.d, L3 = Scale.e, L4 = Scale.f, L5 = Scale.g, L6 = Scale.a1, L7 = Scale.b1,//低音區(qū)
M1 = Scale.c1, M2 = Scale.d1, M3 = Scale.e1, M4 = Scale.f1, M5 = Scale.g1, M6 = Scale.a2, M7 = Scale.b2,//中音區(qū)
H1 = Scale.c2, H2 = Scale.d2, H3 = Scale.e2, H4 = Scale.f2, H5 = Scale.g2, H6 = Scale.a3, H7 = Scale.b3,//高音區(qū)
Y1 = Scale.c3, Y2 = Scale.d3, Y3 = Scale.e3, Y4 = Scale.f3, Y5 = Scale.g3, Y6 = Scale.a4, Y7 = Scale.b4,//極高音
_ = 0XFF//空音
}
static void Main(string[] args)
{
while (true)
{
ShanZhaShu();
}
}
#region 山楂樹
static void ShanZhaShu()
{
Console.WriteLine("《山楂樹》\n演奏:豎琴\n代碼編寫:幽靈眼中的星星");
int[] shanzha =//《山楂樹》音譜數(shù)組,4/4拍(以四分音符為一拍,每小節(jié)四拍)
{
//1 2 3 4 5 6 7 測試部分
0,150,(int)Voice.M1, (int)Voice.M2, (int)Voice.M3, (int)Voice.M4, (int)Voice.M5, (int)Voice.M6,
(int)Voice.M7, (int)Voice.H1, (int)Voice.H1,(int)Voice.M7,(int)Voice.M6,(int)Voice.M5,(int)Voice.M4,
(int)Voice.M3, (int)Voice.M2, (int)Voice.M1, 600,
(int)Voice._,(int)Voice._,(int)Voice._,(int)Voice._,(int)Voice._,(int)Voice._,(int)Voice._,
//前奏
300,(int)Voice.M3,(int)Voice.M6,(int)Voice.M7,900,(int)Voice.H1,300,(int)Voice.M2,(int)Voice.M4,(int)Voice.M6,
900,(int)Voice.M7,300,(int)Voice.L7,(int)Voice.M3,(int)Voice.M4,600,(int)Voice.M7,300,(int)Voice.H1,
(int)Voice.M6,(int)Voice.M3,(int)Voice.M2,(int)Voice.M1,(int)Voice.L7,(int)Voice.L3,(int)Voice.L6,(int)Voice.L6,
(int)Voice.L6,(int)Voice.L6,(int)Voice.L6,(int)Voice.L6,
//part1
600,(int)Voice.L6,300,(int)Voice.L6,600,(int)Voice.M1,150,(int)Voice.L6,(int)Voice.L6,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,600,(int)Voice.M1,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.L6,300,(int)Voice.M3,600,(int)Voice.M3,300,(int)Voice.M1,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.M1,(int)Voice.M1,
600,(int)Voice.M5,300,(int)Voice.M5,(int)Voice.M5,(int)Voice.M4,(int)Voice.M3,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,(int)Voice.M2,(int)Voice.M1,(int)Voice.M2,900,(int)Voice.M3,(int)Voice.M3,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
//Part2
600,(int)Voice.L6,300,(int)Voice.L6,600,(int)Voice.M1,150,(int)Voice.L6,(int)Voice.L6,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,600,(int)Voice.M1,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.L6,300,(int)Voice.M3,600,(int)Voice.M3,300,(int)Voice.M1,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.M1,(int)Voice.M1,
600,(int)Voice.M5,300,(int)Voice.M5,(int)Voice.M5,(int)Voice.M4,(int)Voice.M3,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,(int)Voice.M2,(int)Voice.M1,(int)Voice.M2,900,(int)Voice.M3,(int)Voice.M3,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.L6,300,(int)Voice.M6,(int)Voice.M6,(int)Voice.M5,(int)Voice.M6,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M4,600,(int)Voice.M4,300,(int)Voice.M5,(int)Voice.M6,(int)Voice.M5,(int)Voice.M4,900,(int)Voice.M3,(int)Voice.M3,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.L6,300,(int)Voice.M6,(int)Voice.M6,(int)Voice.M5,(int)Voice.M6,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M4,600,(int)Voice.M4,300,(int)Voice.M5,(int)Voice.M6,(int)Voice.M5,(int)Voice.M4,900,(int)Voice.M3,(int)Voice.M3,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.M6,
//part3
600,(int)Voice.L6,300,(int)Voice.L6,600,(int)Voice.M1,150,(int)Voice.L6,(int)Voice.L6,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,600,(int)Voice.M1,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.L6,300,(int)Voice.M3,600,(int)Voice.M3,300,(int)Voice.M1,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.M1,(int)Voice.M1,
600,(int)Voice.M5,300,(int)Voice.M5,(int)Voice.M5,(int)Voice.M4,(int)Voice.M3,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,(int)Voice.M2,(int)Voice.M1,(int)Voice.M2,900,(int)Voice.M3,(int)Voice.M3,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
//Part4
600,(int)Voice.L6,300,(int)Voice.L6,600,(int)Voice.M1,150,(int)Voice.L6,(int)Voice.L6,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,600,(int)Voice.M1,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.L6,300,(int)Voice.M3,600,(int)Voice.M3,300,(int)Voice.M1,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.M1,(int)Voice.M1,
600,(int)Voice.M5,300,(int)Voice.M5,(int)Voice.M5,(int)Voice.M4,(int)Voice.M3,900,(int)Voice.M2,
600,(int)Voice.L6,300,(int)Voice.L7,(int)Voice.M1,(int)Voice.L7,(int)Voice.M1,(int)Voice.M2,(int)Voice.M1,(int)Voice.M2,900,(int)Voice.M3,(int)Voice.M3,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
600,(int)Voice.L6,300,(int)Voice.M6,(int)Voice.M6,(int)Voice.M5,(int)Voice.M6,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M4,600,(int)Voice.M4,300,(int)Voice.M5,(int)Voice.M6,(int)Voice.M5,(int)Voice.M4,900,(int)Voice.M3,(int)Voice.M3,
600,(int)Voice.M1,300,(int)Voice.M1,(int)Voice.M1,(int)Voice.M2,(int)Voice.M3,900,(int)Voice.M5,
600,(int)Voice.M4,300,(int)Voice.M3,600,(int)Voice.M2,300,(int)Voice.L7,600,(int)Voice.M3,300,(int)Voice.M2,900,(int)Voice.L6,(int)Voice.L6,
};
int sirius_MidiOut = 0;//設(shè)置MIDI設(shè)備句柄變量(播放時(shí)使用)
midiOutOpen(out sirius_MidiOut, 0, 0, 0, 0);//打開MIDI設(shè)備,將設(shè)備賦值給句柄變量
int volume = 0x7F;//設(shè)置音量最高,0-0x7F,0為最低音量
int sleep = 300;//設(shè)置單個(gè)音的持續(xù)時(shí)長,默認(rèn)為單拍時(shí)長,本樂曲美聲歌唱版本每分鐘100拍,單拍時(shí)長600ms,經(jīng)我測試,對于電音來說太過緩慢,300ms更為合適。
int voice = 0x0;//定義需要MIDI播放的音的參數(shù)(非常重要)
foreach (var element in shanzha)//遍歷歌曲的音譜數(shù)組,播放每個(gè)音
{
//每個(gè)音播放前,設(shè)置空拍及音符持續(xù)時(shí)長標(biāo)志,根據(jù)本樂曲設(shè)置,一拍為300ms
if (element == 150) { sleep = 150; continue; }//接下來的每個(gè)音時(shí)長為半拍
if (element == 300) { sleep = 300; continue; }//接下來的每個(gè)音時(shí)長為一拍
if (element == 600) { sleep = 600; continue; }//接下來的每個(gè)音時(shí)長為兩拍
if (element == 900) { sleep = 900; continue; }//接下來的每個(gè)音時(shí)長為三拍
if (element == 1200) { sleep = 1200; continue; }//接下來的每個(gè)音時(shí)長為四拍
if (element == 1500) { sleep = 1500; continue; }//接下來的每個(gè)音時(shí)長為五拍
if (element == 1800) { sleep = 1800; continue; }//接下來的每個(gè)音時(shí)長為六拍
if (element == 0) { Thread.Sleep(150); continue; }//馬上停半拍,空半拍
if (element == 255) { Thread.Sleep(300); continue; }//255就是0xFF,枚舉變量中的 Voice._ ,即馬上停一拍,空拍
voice = (volume << 16) + (element << 8) + 0x91;//設(shè)置需要播放的音的參數(shù),volume為音量,element為需要播放的音高(來自音譜數(shù)組元素),0x91,9為開始吹奏,1為在第一個(gè)聲音通道進(jìn)行(通道從0x90-0x9F,共16個(gè)通道,都可以使用)。
Console.WriteLine(voice);
midiOutShortMsg(sirius_MidiOut, 40 << 8 | 0xC1);//設(shè)置音色(我這里是豎琴,編號為46,可以查MIDI音色表,100多種聲音都可以用),0xC1,C為設(shè)置,被設(shè)置的通道為1通道(通道從0xC0-0xCF,共16個(gè)通道,都可以設(shè)置)
midiOutShortMsg(sirius_MidiOut, voice);//吹奏voice代表的音
Thread.Sleep(sleep);//音符持續(xù)時(shí)長
midiOutShortMsg(sirius_MidiOut, (volume << 16) + (element << 8) + 0x81);//停止該音的吹奏
}
midiOutClose(sirius_MidiOut);//關(guān)閉MIDI設(shè)備
}
#endregion
}
}