整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          音樂研發必備:理解 MIDI 協議與標準 MIDI 文件格式

          . MIDI 簡介

          MIDI 協議即數字音樂接口(Musical Instrument Digital Interface),是電子樂器、合成器等演奏設備之間的一種即時通信協議,用于硬件之間的實時演奏數據傳遞。MIDI 協議誕生之初希望解決的事情是通過統一通信協議讓不同樂器制造商的設備可以互相兼容,比如把 Roland 鍵盤接入 Yamaha 合成器。MIDI 協議的編碼經過拓展后也可以作為一種記錄音樂信息的文件格式,被稱為“標準 MIDI 文件格式”。

          在音樂技術研發中除了需要與音頻打交道之外,許多場景中還需要直接處理音符信息。如果說 wav 與 mp3 記錄的是音樂的物理現象,那么 MIDI 協議與 MIDI 文件則記錄的是音樂這門語言的“文字”。本文的目的是讓開發中涉及到音樂“本體”的同學可以了解這一最通用的演奏信息交互和文件存儲格式的編碼規則。同時通過對 MIDI 事件流等概念的認識,能在開發中更好地抽象自己的業務邏輯。

          1.1 MIDI 數據流 & 編碼

          和 HTTP 這類協議不同,MIDI 作為傳輸協議時所有傳遞的信息都需要被實時響應,比如一個觸鍵信息、一個效果器參數的改變都需要立刻被執行,所以其采用數據流的方式進行數據傳輸。MIDI 定義了一個 8 位的二進制數據流,許多時候我們可以使用 ASCII 碼來將其表示為 16 進制的字符用于傳輸和保存。

          對于 MIDI 標準文件格式來說,其存儲的內容也是 MIDI 產生的事件流。一段典型的 MIDI 文件長這樣:

          4D 54 68 64 00 00 00 06 00 01 00 03 01 E0 4D 54
          72 6B 00 00 00 1A 00 FF 03 03 31 32 33 00 FF 51
          03 08 7A 23 00 FF 58 04 04 02 18 08 00 FF 2F 00
          ...

          上面這個例子可能會造成一些困惑,因為 MIDI 文件確實對人類閱讀不太友好,但其編碼規則實際上是較易掌握的,下面我們就來逐步認識 MIDI 的編碼規則。

          注:在本文中,一個字節的最低有效位為第 0 位,最高有效位是第 7 位。比如在 X000 000Y 中,X 為第 7 位,Y 為第 0 位。

          1.2 MIDI 消息

          MIDI 最核心的功能是用于傳輸實時的音樂演奏信息,這些信息本質上是一條條包含了音高、力度、效果器參數等信息的指令,我們將這些指令稱之為 MIDI 消息(MIDI message)。一條 MIDI 消息通常由數個字節組成,其中第一個字節被稱為 STATUS byte,其后面有跟有數個 DATA bytes。STATUS byte 第七位為 1,而 DATA byte 第七位為 0。

          開頭的 STATUS byte 有兩個作用:一個作用是表示系統或者某個信道狀態的改變,其二個作用是確定當前 MIDI Message 的類型,MIDI 類型會確定后面 DATA byte 的數量和意義。這樣說比較空洞,下面我們舉一個例子:

          Status byte : 1100 CCCC
          Data byte 1 : 0XXX XXXX
          Status byte : 1001 CCCC
          Data byte 1 : 0PPP PPPP
          Data byte 2 : 0VVV VVVV

          第一個 STATUS byte 告訴我們這是一個進行樂器選擇的 MIDI Message(1100 為樂器選擇指令,CCCC 是信道編號)。樂器選擇的 MIDI Message 只有一條 DATA byte,而這條 DATA Byte 的數據表示選擇的樂器編號。第二條 1001 開頭的 STATUS byte 則告訴我們這是一條 Note On 類型 MIDI message,這個類型按照約定有兩個 DATA byte。

          除了向整個系統發送的 MIDI 消息, STATUS byte 通常包含了信道編號(即例子中的 CCCC),16 個信道分別從 0000 到 1111。而向整個系統發送的 MIDI 信息則以 1111 開頭,原來的信道編號變成了指令編號(比如播放指令:1111 1010,終止指令:1111 1100)。

          需要注意的是,許多時候我們會連續發送許多相同狀態的 MIDI 消息,這個時候可以省略 STATUS byte,合成器會沿用最后一個接收的 STATUS byte,被合成器記錄的狀態稱之為 MIDI RUNNING STATUS。

          總結一下:

          一條 MIDI message 由 STATUS byte 和 Data byte 構成。

          STATUS byte 以 1 開頭,DATA byte 以 0 開頭。

          STATUS byte 確定消息的類型。后面的 DATA 字節數取決于消息的類型。

          STATUS byte 通常包含信道編號,除了面向系統發送的指令。

          連續相同的 STATUS byte 可以省略。

          2. 常用 MIDI Message

          MIDI Message 不需要全部掌握,需要的時候可以直接到 MIDI 標準中查詢,日常開發中只需要了解常用的幾種 MIDI Message 即可。下面筆者介紹最常用的幾種 MIDI Message。

          2.1 NOTE ON & NOTE OFF - 音符的觸發與終止

          NOTE ON 和 NOTE OFF 是最主要的兩個 MIDI Message。當演奏者敲擊音樂鍵盤的琴鍵時發送 NOTE ON 消息,它包含了音高以及“力度”的參數。當合成器收到此消息時,它會開始以相應的音高和“力度”播放該音符。當收到 NOTE OFF 消息時,合成器會終止該音符。

          每個 NOTE ON 消息都需要相應的 NOTE OFF 消息,否則該音符將一直處于播放狀態。但打擊樂器可以只發送 NOTE ON,因為打擊樂音符會自動停止。但最好養成始終發送 NOTE OFF 的習慣,因為不同合成器對這一特性的實現可能不一樣。

          下面我們舉例說明 NOTE ON:

          Status byte : 1001 CCCC
          Data byte 1 : 0PPP PPPP
          Data byte 2 : 0VVV VVVV

          在這個例子中,1001 可以理解為 NOTE ON 事件的編碼,CCCC 是信道編號。

          PPP PPPP 表示音高值,在 General MIDI 協議中(后文會提到),通常使用 69 表示標準音 A4(440 Hz),音高值增減一,就增減一個半音。比如 60 表示 C4(中央 C), 61 表示 C#4。同樣,升高或者降低八度只需要在當前音高上增減 12 即可。

          VVV VVVV 表示速率(velocity),這個速率可以理解為敲擊鍵盤的速度,或者管樂器氣流的速度。在最基礎的合成器中,速率僅用于確定彈奏音符的力度,唯一的效果是音符音量變大或變小。總體來說,下面這張表可以作為速率和樂譜中的力度記號的對應關系參考:

          但在在一些復雜的仿真建模合成器中,速率也會影響音色。我們以 Galaxy Steinway 采樣器為例:

          圖片來源:https://zhuanlan.zhihu.com/p/19964066

          左側是小力度敲擊的頻域圖,右側是大力度敲擊的頻域圖。我們可以看到大力度敲擊不僅產生了更多的泛音,也在低頻區產生了一些噪音(木材被撞擊的聲音)。

          注:關于 velocity 可以參考附錄的介紹。

          NOTE OFF 消息和 NOTE ON 消息基本一樣:

          Status byte : 1000 CCCC
          Data byte 1 : 0PPP PPPP
          Data byte 2 : 0VVV VVVV
          

          其中 CCCC 和 PPPPPPP 含義同上。VVVVVVV 是釋放速率,可以看作是按鍵抬起的速度,這個值很少使用,通常將其設置為零。另外,在實踐中經常使用速率為 0 的 NOTE ON 消息取代 NOTE OFF 消息。

          需要額外說明的是 MIDI 協議還提供了一組 All Notes Off 消息,當某個信道接收到 All Note Off 消息之后會關閉所有還在發音的振蕩器,通常來說 All Notes Off 消息用于在演奏、播放結束后用于清理狀態,這里不多贅述。

          2.2 樂器選擇

          樂器選擇消息的格式如下:

          Status byte : 1100 CCCC
          Data byte 1 : 0XXX XXXX
          

          其中唯一的一個 DATA byte 表示樂器編號,支持 128 個不同的樂器。由于不同的軟件上存在的樂器音源并不一致,為了讓 A 設備上創建的標準 MIDI 文件在 B 設備上播放時聽起來相似,樂器廠商邊采用 General MIDI 協議來編排音源。Gerneral MIDI 通常簡寫為 GM ,它提供了一個標準化的音庫,將 128 個樂器排列成 16 個系列,每個系列有 8 個同類型的樂器,并為每個樂器分配一個特定的程序編號。GM 樂器表可以參考:

          http://www.harfesoft.de/aixphysik/sound/midi/pages/genmidi.html

          在 GM 標準下,信道 10 是保留給打擊樂器的(實際上合成器可以在任何信道上使用鼓),在這個信道上樂器編碼遵循通用 MIDI 鼓樂器列表(General MIDI drum instruments list),具體可以參考:

          https://en.wikipedia.org/wiki/General_MIDI#Percussion

          由于鼓是總體上是噪音樂器,所以之前的音高參數則被映射為不同的鼓音效。

          注:噪音樂器指沒有明確音高的樂器,有明確音高的樂器稱為樂音樂器。

          2.3 控制器消息

          MIDI 設備通常會提供一些控制器用于改變合成器的某個參數,比如混響、增益等。MIDI 協議可以使用控制器消息操作 128 個不同的控制器,控制器消息結構如下:

          Status byte : 1011 CCCC
          Data byte 1 : 0NNN NNNN
          Data byte 2 : 0VVV VVVV
          

          其中 NNN NNNN 是控制器的編號,VVV VVVV 則是控制器的值。

          控制器消息一方面可以用于改變合成器的某些參數,比如我們可以用以下指令將某個信道的力度值設置為 100:

          Status byte : 1011 CCCC
          Data byte 1 : 0000 0111
          Data byte 2 : 0110 0100
          

          另一方面,控制器編碼可以通過“組合”的方式實現一些更復雜的指令。如前文所述,選擇樂器可以通過 1000 開頭的 STATUS byte 實現,這個指令可以選擇 128 種樂器。對于同一個樂器來說可以應用不同的音色庫,比如我可以在鋼琴上使用雅馬哈的采樣、施坦威的采樣或者是珠江的采樣,由于樂器廠商認為 128 這個數量對于音色庫太小了,所以采用的 MSB + LSB 的方式表示音色庫,例子如下:

          Status byte : 1011 CCCC
          Data byte 1 : 0000 0000   // 0 = Sound bank selection (MSB)
          Data byte 2 : 0000 0101
          
          Status byte : 1011 CCCC
          Data byte 1 : 0010 0000   // 32 = Sound bank selection (LSB)
          Data byte 2 : 0000 0001
          
          Status byte : 1100 0000
          Data byte 1 : 0000 0010

          這段代碼選擇了一個編號為 2,并且音色編號為 MSB = 0, LSB = 32 的樂器。由于 MSB 和 LSB 的范圍都是 2 ^ 7 = 128,所以理論上可以選擇的音色為 (2 ^ 7) ^ 2 = 16384

          在 MIDI 中控制器消息和音源與效果器的參數密切相關,不同編號的控制器有一些約定俗稱的含義,在程序中實現控制器時盡量與已有的規范對齊,具體內容可以參考這個表格:MIDI CC List(https://professionalcomposers.com/midi-cc-list/)

          注:MSB 指最高有效字節(most significant byte),LSB 指最低有效字節(least significant byte)。一個 14 位的數據 XXX XXXX YYY YYYY 可以用 MSB + LSB 表示為:0XXX XXXX 0YYY YYYY

          2.4 彎音消息

          彎音消息也用到了我們剛才提到的 MSB + LSB 表示法,其消息結構如下:

          Status byte : 1110 CCCC
          Data byte 1 : 0LLL LLLL
          Data byte 2 : 0MMM MMMM

          其中 LLL LLLL 表示 LSB,MMM MMMM 表示 MSB,彎音值 0x2000(即 0b10000000000000)為同音高,0x3FFF(即 0b11111111111111)表示上方大二度,0x0000(即 0b00000000000000)表示下方大二度。在實踐中,我們可以通過連續發送遞增或者遞減的彎音消息來表現滑音。

          2.5 系統獨占消息

          所有系統消息都以 1111 開頭,其中有兩個特殊的消息。一個是 1111 0000 它表示后面的消息是系統獨有的。另外一個 1111 0111 則表示系統獨有消息結束,消息結構如下:

          11110000
          0iiiiiii
          0ddddddd
          ..
          ..
          0ddddddd
          11110111
          

          當合成器監聽到 1111 0000 時,檢查下一個字節 0iii iiiiiii iiii 是一個 7 位的制造商 ID。如果合成器識別出這個代碼則會繼續監聽后面的數據,否則則忽略掉收到的消息,直到結束消息 1111 0111 出現。

          3. 宿主的 MIDI API

          許多宿主環境都提供了用于編寫 MIDI 交互程序的 API,在瀏覽器上是 Web MIDI API,在 iOS & Mac 上是 Core MIDI,Android 上則有 AMidi。為了方便讀者進行實際操作,我們以 Web MIDI API 為例展示如何編寫一個最基本的 MIDI 程序:

          const button = document.getElementById('console-message')
          
          button.addEventListener('click', () => {
            if (navigator.requestMIDIAccess) {
              navigator.requestMIDIAccess()
                .then(success, failure);
            }
          })
          
          function success (midiAccess) {
              const inputs = midiAccess.inputs.values();
              for (let input of inputs) {
                  input.value.onmidimessage = onMIDIMessage;
              }
          }
          
          function failure () {
              console.error('No access to your midi devices.')
          }
          
          function onMIDIMessage (messageEvent) {
            console.log(messageEvent)
          }
          

          在這里,我們可以通過 requestMIDIAccess 向用戶索要訪問 MIDI 設備的權限,用戶允許后我們會拿到一個 midiAccess 對象,可以通過這個對象拿到所有的輸入和輸出設備。我們可以通過設備對象提供的 onmidimessage 回調監聽 midi message。

          MIDI 消息的編碼存儲在 messageEvent 的 data 成員中,通過打印出的信息我們可以發現 Web MIDI API 并不會省略 Status Byte,這是為了便于開發者更容易區分指令屬于哪個狀態,而不必手動保存 MIDI 的運行狀態。

          如果想要 MIDI 可以發音,我們可以使用 Web Audio API 提供的振蕩器:

          const button = document.getElementById('play-sound')
          const oscillators = {};
          let context
          
          
          button.addEventListener('click', () => {
            context = new AudioContext()
          
            if (navigator.requestMIDIAccess) {
              navigator.requestMIDIAccess()
                .then(success, failure);
            }
          })
          
          function success (midiAccess) {
              const inputs = midiAccess.inputs.values();
          
              for (let input of inputs) {
                  input.onmidimessage = onMIDIMessage;
              }
          }
          
          function failure () {
              console.error('No access to your midi devices.')
          }
          
          function onMIDIMessage (message) {
              const frequency = midiNoteToFrequency(message.data[1]);
          
              // midi 鍵盤的普通按鍵默認使用通道 0,所以其 note on 事件為 1100 0000
              if (message.data[0] === 144) {
                  playNote(frequency);
              }
              
              // note off
              if (message.data[0] === 128) {
                  stopNote(frequency);
              }
          }
          
          function midiNoteToFrequency (note) {
              return Math.pow(2, ((note - 69) / 12)) * 440;
          }
          
          function playNote (frequency) {
              oscillators[frequency] = context.createOscillator();
              oscillators[frequency].frequency.value = frequency;
              oscillators[frequency].connect(context.destination);
              oscillators[frequency].start(context.currentTime);
          }
          
          function stopNote (frequency) {
              oscillators[frequency].stop(context.currentTime);
              oscillators[frequency].disconnect();
          }
          

          我們可以使用這個小程序來回顧與驗證我們之前講到的 MIDI Message 知識。

          這里有一個筆者以前做的視唱練耳小工具,可以使用 MIDI 鍵盤進行視唱練耳練習:

          演示地址:muse-training(https://muse-training-8gwn0lc039762917-1252681582.tcloudbaseapp.com/)

          倉庫地址:https://github.com/lipd/muse-training

          4. 標準 MIDI 文件格式規范

          MIDI 協議解決的是音樂設備之間的即時通訊問題,它本質上是一個硬件之間的通信協議。而當我們想把 MIDI 演奏保存在磁盤上則需要用到標準 MIDI 文件格式規范(Standard MIDI-File Format Spec)。和 MIDI 通信協議一樣,MIDI 文件也是 8 位字節流,下文將會說明 MIDI 文件一些最基本的格式規范。

          4.1 Chunk

          Chunk 是構成 MIDI 文件的基本單元。一個 Chunk 由三個部分組成:Chunk 類型 、Chunk 長度以及 Chunk 數據。Chunk 類型是 4 個 ASCII 字符,之后使用 32 位表示 Chunk 數據的長度,最后才是 Chunk 需要存儲的數據。

          MIDI 中一共有兩種 Chunk,分別為 Header Chunk 和 Track Chunk。Header Chunk 標記為 MThd,存儲的是整個 MIDI 文件的基本信息,和 PNG 等文件的 Header Chunk 類似。Track Chunk 標記為 MTrk,每個 Track Chunk 都存儲了一個 MIDI 事件流,一個事件流可以包含 16 個 MIDI 信道的消息。一個典型 MIDI 文件的結構如下:

          MThd <length>
          <MThd data>
          MTrk <length>
          <MTrk data>
          MTrk <length>
          <MTrk data>

          4.2 Header Chunk

          MIDI 文件的 Header Chunk 包含的信息非常簡單,我們以上面這個文件為例:

          4D 54 68 64    // MThd 的 ASCII 碼
          00 00 00 06    // MThd 的數據長度,MThd Data 固定為 6 字節
          ---- DATA 部分 ----
          00 01          // MIDI 文件格式,有 0、1、2 三種
          00 02          // MIDI 文件的包含的音軌數量,即 Track Chunk 數量
          00 DC          // MIDI 文件的時間類型

          前兩條數據已經介紹過,這里不再贅述。我們來解釋一下 MIDI 文件格式與 MIDI 時間類型:

          MIDI 文件格式(MIDI File Formats)

          MIDI 文件格式分為三種,格式 0 的 MIDI 文件只有一個 Header Chunk 和一個 Track Chunk。對于只有一個軌道的程序可以采用這種格式。

          格式 1 有一個 Header Chunk ,和多個 Track Chunk 。其中第一條 Track Chunk 是特殊的,負責記錄 MIDI 文件的所有 Meta Event(后面會講到),而從第二條 Track Chunk 開始才會記錄 MIDI Event,所以我們上圖中的 MIDI 文件實際上只有一條用于演奏的音軌。目前絕大部分的支持多音軌的程序都采用這種格式,筆者也建議讀者盡量使用這種格式。

          格式 2 的 MIDI 文件也有多個 Track Chunk,但不同的是格式 1 所有 Track Chunk 共用一條時間軸,所有 Track 應當被視作同時播放的。而格式 2 中 Track Chunk 都有自己獨立的時間信息,這種格式非常少見,不建議使用。

          我們用一張表總結一下:


          音軌數量

          時間軸

          格式 0

          1 個

          1 條

          格式 1

          多個

          1 條

          格式 2

          多個

          多條

          MIDI 時間類型

          MIDI 時間類型主要有兩種,為了方便介紹讀者可以簡單將其理解為“按音符分割的”和“按幀分割的”:

          “按音符分割的”時間類型 15 位為 0,被稱為 TPQN(Ticks Per Quarter-Note),即一個四分音符中包含了多少 Tick。在前文的例子中 00 DC 表示 TPQN 為 220,那么一個八分音符為 110 Ticks,一個二分音符為 440 Ticks。另外 TPQN 也被稱為 Pulses Per Quarter-Note (每四分音符的脈沖數),如果你在代碼中看到 PPQ、PPQN 這樣的簡寫,你知道他們是一個意思即可。

          “按幀分割的”時間類型 15 為 1,這種格式單純 MIDI 文件中幾乎不用而且比較復雜,建議讀者跳過。其編碼規則簡單說就是使用了 SMPTE 時間碼的規范。其 14 - 8 位包含了包含 -24、-25、-29 或 -30 四個值之一,對應于四種標準 SMPTE 時間碼格式(-29 對應于 30 個丟幀),并表示每秒的幀數。第 7 到 0 位表示幀內分辨率。我們依然用一張表總結一下:


          15 位

          14-8位

          7-0位

          按音符

          0

          四分音符的Tick數

          按幀

          1

          SMPTE格式

          每幀Tick數

          4.3 Track Chunk

          Track Chunk 的主要功能是用于存儲實際的演奏數據。它的 Chunk Data 中存儲的是一串事件流,被 Track Chunk 記錄的事件我們稱為 MTrk 事件,其結構如下:

          <MTrk event> = <delta time> <event>

          在這個結構中,事件可以指代三類事件:midi 事件、系統獨有事件、元事件:

          <event> = <midi event> | <sysex event> | <meta event>

          delta time

          MIDI 通信時所有信息都是即時執行,所以 MIDI 消息并沒有記錄時間,但是 MIDI 文件則需要記錄時間在時間軸上的位置。MIDI 文件采用差量時間來記錄 MIDI 事件,即 Δt。delta time 表示的是當前事件與上一個時間相差的 Tick 數。如果要表示同時發生的數個任務,則記錄一串 delta time 為 0 的事件流即可。比如我們控制器一章中切換樂器的事件流可以表示為:

          Delta time  : 0000 0000
          Status byte : 1011 CCCC
          Data byte 1 : 0000 0000   // 0 = Sound bank selection (MSB)
          Data byte 2 : 0000 0101
          
          Delta time  : 0000 0000
          Status byte : 1011 CCCC
          Data byte 1 : 0010 0000   // 32 = Sound bank selection (LSB)
          Data byte 2 : 0000 0001
          
          Delta time  : 0000 0000
          Status byte : 1100 0000
          Data byte 1 : 0000 0010

          sysex event

          即系統獨占的消息事件,具體可以參考前文中的系統獨占消息。

          meta event

          所有元事件以 1111 1111 開頭,這個指令在 MIDI 消息中表示系統復位。這個指令是一個系統實時信息,通常在使用 MIDI 文件的程序并不會用到,所以在這里用于表示元事件。元事件主要用于指定拍號、調號、速度等。

          需要注意的是FF 2F 00 是一個特殊的元事件,表示軌道結束。所有 Track Chunk 都以這個元事件結束。下面這張表是標準中已定義的元事件:


          意義

          FF 00 02

          序列號

          FF 01 len text

          文本事件

          FF 02 len text

          版權聲明

          FF 03 len text

          軌道名稱

          FF 04 len text

          軌道中使用的樂器類型

          FF 05 len text

          歌詞

          FF 06 len text

          某個點的名稱,比如“第一樂章”

          FF 07 len text

          Cue Point 某個舞臺事件描述

          FF 20 01 cc

          MIDI 通道前綴

          FF 2F 00

          End of Track

          FF 51 03 tttttt

          設置速度

          FF 54 05 hr mn se fr ff

          SMPTE Offset

          FF 58 04 nn dd cc bb

          拍號

          FF 59 02 sf mi

          調號

          5. MIDI 協議的缺陷與改良方案

          5.1 MIDI 2.0 & MPE

          MIDI 通信協議目前看來主要有兩個較明顯的缺陷。第一個缺陷是許多值可以表示的范圍實在有限,比如 note off 的 velocity 就只有 128 個、樂器也只有 128 個、只有 16 個信道。

          另一個問題更為麻煩,MIDI 中控制器、和彎音消息只能發送給某個信道,你根本就沒法將它和某個音聯系在一起。這一局限在以前并沒有引起多少問題,因為傳統樂器很少碰到按音處理控制器的情況。而彎音用得最頻繁的更多是單聲部樂器。

          但電子音樂界向來不缺乏整活健將,工程師總是會想方設法突破現有限制。最典型的例子就是 seaboard 鍵盤,這玩意兒可以在每個鍵上提供彎音能力。你可以從下面這段演奏上感受到這一樂器的神奇魅力:

          原視頻鏈接:https://www.youtube.com/watch?v=6SCug5kUsBs

          為了解決讓控制器消息能按“音”發送,seaboard 的制造商 ROLI 制訂了 MIDI Polyphonic Expression(MPE,MIDI 復音表示法)。其原理基本上可以概括為:讓每個發聲的音符都會在其 Note On 和 Note Off 之間臨時分配一個 MIDI 通道。這樣便把控制器消息和彎音消息與特定音符建立了聯系,并且很好的兼容了 MIDI 協議。

          上述問題現在都正在通過新的 MIDI 2.0 得到解決,在 MIDI 2.0 中 volocity 從 0 - 128 擴展到 0 - 65535,信道從 16 個增加到 256 個,同時 MIDI 2.0 也支持 MPE 以及遠程控制。

          5.2 如何拓展 MIDI

          如果 MIDI 2.0 和 MPE 這類現成的解決方案無法滿足你的需求,那么你可以考慮自己來拓展 MIDI 協議或者 MIDI 標準格式。目前來看,可靠的拓展方式有幾下幾種:

          1. 使用未定義的 MIDI 消息:比如系統消息 1111 0101 的行為在 MIDI 標準中就未被定義。這種方法的好處是不需要進行額外的解析工作,但缺點便是可以使用的指令十分有限。
          2. 使用自定義 Chunk:Chunk 在設計之初便考慮到了拓展的問題,你可以按照 Chunk 的格式自由地聲明一個新的 Chunk 類型,主流解析工具在碰到無法解析的 Chunk 時會自動忽略掉,所以不用擔心兼容的問題。如果你有整段的數據,既不屬于 Track,又不能被 Heaer 所包含,那么可以考慮這種方式。
          3. 使用系統獨占消息:如果你需要在 MIDI 通信協議上進行拓展,可以考慮使用系統獨占消息,合成器會自動忽略無法解析的獨占消息。具體可以參考附錄中的系統獨占消息一節。
          4. 其他:你也可以參考 MPE 的方式,基于現有的編碼方案但是重新定義指令的意義和執行。

          6. 思考與討論

          6.1 什么時候使用 MIDI 格式,什么時候不用?

          首先我們需要認識到 MIDI 的優點,MIDI 記錄的實際上是事件流,最適合的場景就是在現場演奏時用于硬件之間的通信。作為 MIDI 文件格式作為一種存儲格式,其優點是數據十分緊湊,體積較小。但 MIDI 的缺點是十分明顯的,一方面我們無法快速查詢、訪問其中某個具體內容的值:比如我們沒法快速找到某一個軌道的拍號,或者某個音的音高。

          所以我的建議是,盡量避免在現場演奏場景之外使用 MIDI 文件格式,但可以在抽象上對齊 MIDI。在內存中我們盡量把 MIDI 文件轉化為實例對象,便于我們快速訪問。在需要持久化的場景下則可以使用更容易解析的 JSON 或者 MusicXML 格式。只有在用戶需要或者向其他編輯工具導出數據的時候,我們才考慮使用 MIDI 標準文件格式。

          6.2 MIDI 協議無法滿足的需求如何解決?

          絕大部分這類問題可以通過不使用 MIDI 編碼來解決。原則很簡單,只要不涉及現場演奏場景和向其他工具導出數據,就避免使用 MIDI 編碼來做任何事情。只用確保在需要 MIDI 的場景可以導出 MIDI 文件就行。

          6.3 如果多數場景不使用 MIDI,那有必要深入學習 MIDI 協議嗎?

          如果你的開發工作涉及到音樂的“本體”部分,那么我建議多了解一些 MIDI 協議,因為雖然我們可能多數情況下不直接使用 MIDI 協議的編碼,但是 MIDI 的事件流是創作場景和存儲場景會大量用到的,同時 MIDI 中的多數抽象和概念是行業內通用的。

          6.4 如何設計自定義的音樂數據格式?

          我的建議是用一個文檔維護所有的基礎字段和拓展字段,各項目在定義 Model 時盡量參考這個文檔。如果現有的拓展字段可以解決你的需求,就不要新增拓展字段。

          附錄

          可變長度數量(Variable-Length Quantities)

          由于單個字節表示的最大范圍為 0 - 256,所以在 MIDI 文件中表示較大數字時會采用可變長度數量。其每一個字節使用第 7 位表示這個字節是否為最后一個字節,1 表示不是最后一個字節,0 表示是最后一個字節, 0 - 6 位則作為有效位。

          舉一個例子,數字 127 可以表示為 0111 1111 ,128 則表示為 1000 0001 0000 0000,這樣理論上可以表示的數字可以無限大,不過在實踐中通常不會使用超過 32 位。

          總結一下就是:

          7 位

          0-6 位

          是否為最后一個字節

          有效位

          速率的解釋

          note on 中的 velocity 實際上是按鍵的“觸發速率”,你可以把其視為從鍵盤能感知到下按到下按結束這個過程中的鍵程除以按下時間,note off 則是反向的“釋放速率”。速率的計算方式和更多細節可以參考這篇論文:The Interpretation of MIDI Velocity

          一堆速查表

          • 查十進制的 MIDI 消息:Expanded MIDI 1.0 Messages List (Status Bytes)
          • 查 GM 樂器表:General MIDI Instrument List
          • 查 MIDI 事件流:Standard MIDI-File Format Spec. 1.1, updated

          參考文獻

          • https://www.midi.org/specifications
          • MIDI Tutorial
          • Standard MIDI-File Format Spec. 1.1, updated
          • MIDI Polyphonic Expression (MPE) Specification Adopted
          • 鋼琴的觸鍵方式是如何影響彈出來的音色的?(https://zhuanlan.zhihu.com/p/19964066)
          • MIDI Tick、Meta-event、變長數表示法、區分 MIDI 文件中單個字節的含義(https://www.cndzq.com/bbs/thread-117332-1-1.html)
          • MPE in Live 11(https://help.ableton.com/hc/en-us/articles/360019144999-MPE-in-Live-11)
          • GDX-620 使用說明書(https://de.yamaha.com/files/download/other_assets/9/334239/DGX-620_ZH.pdf)

          推薦讀物

          • 《音樂聲學——音響、樂器、計算機音樂、MIDI、音樂廳聲學原理及應用》- 龔鎮雄
          • The Computer Music Tutorial - Curtis Roads

          tml由element組成。element分兩種,正常element和空element。

          正常element

          <p class=“wd”>world</p>

          element由tag和content構成

          • tag
          1. 開始tag <p class=“wd”>
          2. 結束tag</p>
          • content
          1. 可以是直接在頁面上顯示的內容
          2. 也可包含別的tag

          <ul>

          <li>first</li>

          <li>second</li>

          </ul>

          空element

          沒有content和結束tag的element。比如

          1. 換行<br>
          2. <img src=“bg.png”>
          3. <input type=“text” name=“name”>

          element之間存在三種關系

          1. 父子關系。
          2. 祖孫關系
          3. 兄弟關系

          <div>

          <h1>title</h1>

          <p>p1</p>

          <p>p<strong>2</strong ></p>

          </div>

          div和h1,div和p,p和strong是父子關系。

          div和strong是祖孫關系。

          h1和p是兄弟關系。

          eb 上的多媒體指的是音效、音樂、視頻和動畫。

          現代網絡瀏覽器已支持很多多媒體格式。

          什么是多媒體?

          多媒體來自多種不同的格式。它可以是您聽到或看到的任何內容,文字、圖片、音樂、音效、錄音、電影、動畫等等。

          在因特網上,您會經常發現嵌入網頁中的多媒體元素,現代瀏覽器已支持多種多媒體格式。

          在本教程中,您將了解到不同的多媒體格式,以及如何在您的網頁中使用它們。

          瀏覽器支持

          第一款因特網瀏覽器只支持文本,而且即使是對文本的支持也僅限于單一字體和單一顏色。隨后誕生了支持顏色、字體和文本樣式的瀏覽器,圖片支持也被加入。

          不同的瀏覽器以不同的方式處理對音效、動畫和視頻的支持。某些元素能夠以內聯的方式處理,而某些則需要額外的插件。

          多媒體格式

          格式 多媒體元素(比如視頻和音頻)存儲于媒體文件中。

          確定媒體類型的最常用的方法是查看文件擴展名。當瀏覽器得到文件擴展名 .htm 或 .html 時,它會假定該文件是 HTML 頁面。.xml 擴展名指示 XML 文件,而 .css 擴展名指示樣式表。圖片格式則通過 .gif 或 .jpg 來識別。

          多媒體元素元素也擁有帶有不同擴展名的文件格式,比如 .swf、.wmv、.mp3 以及 .mp4。

          視頻格式

          MP4是互聯網推出新的視頻格式。YouTube 推薦使用 MP4 。Flash Players 支持 MP4HTML5 支持 MP4。
          格式文件描述
          AVI.aviAVI (Audio Video Interleave) 格式是由微軟開發的。所有運行 Windows 的計算機都支持 AVI 格式。它是因特網上很常見的格式,但非 Windows 計算機并不總是能夠播放。
          WMV.wmvWindows Media 格式是由微軟開發的。Windows Media 在因特網上很常見,但是如果未安裝額外的(免費)組件,就無法播放 Windows Media 電影。一些后期的 Windows Media 電影在所有非 Windows 計算機上都無法播放,因為沒有合適的播放器。
          MPEG
          • .mpg
          • .mpeg
          MPEG (Moving Pictures Expert Group) 格式是因特網上最流行的格式。它是跨平臺的,得到了所有最流行的瀏覽器的支持。
          QuickTime.movQuickTime 格式是由蘋果公司開發的。QuickTime 是因特網上常見的格式,但是 QuickTime 電影不能在沒有安裝額外的(免費)組件的 Windows 計算機上播放。
          RealVideo
          • .rm
          • .ram
          RealVideo 格式是由 Real Media 針對因特網開發的。該格式允許低帶寬條件下(在線視頻、網絡電視)的視頻流。由于是低帶寬優先的,質量常會降低。
          Flash
          • .swf
          • .flv
          Flash (Shockwave) 格式是由 Macromedia 開發的。Shockwave 格式需要額外的組件來播放。但是該組件會預裝到 Firefox 或 IE 之類的瀏覽器上。
          Mpeg-4.mp4Mpeg-4 (with H.264 video compression) 是一種針對因特網的新格式。事實上,YouTube 推薦使用 MP4。YouTube 接收多種格式,然后全部轉換為 .flv 或 .mp4 以供分發。越來越多的視頻發布者轉到 MP4,將其作為 Flash 播放器和 HTML5 的因特網共享格式。
          最新的 HTML5 標準只支持 MP4, WebM, 和 Ogg 視頻格式。

          聲音格式

          MP3是一種音頻壓縮技術,其全稱是動態影像專家壓縮標準音頻層面3(Moving Picture Experts Group Audio Layer III),簡稱為MP3。它被設計用來大幅度地降低音頻數據量。如果你的站點是音樂類型的,你可以選擇mp3格式。

          格式文件描述
          MIDI
          • .mid
          • .midi
          MIDI (Musical Instrument Digital Interface) 是一種針對電子音樂設備(比如合成器和聲卡)的格式。MIDI 文件不含有聲音,但包含可被電子產品(比如聲卡)播放的數字音樂指令。點擊這里播放 The Beatles。因為 MIDI 格式僅包含指令,所以 MIDI 文件極其小巧。上面的例子只有 23k 的大小,但卻能播放將近 5 分鐘。MIDI 得到了廣泛的平臺上的大量軟件的支持。大多數流行的網絡瀏覽器都支持 MIDI。
          RealAudio
          • .rm
          • .ram
          RealAudio 格式是由 Real Media 針對因特網開發的。該格式也支持視頻。該格式允許低帶寬條件下的音頻流(在線音樂、網絡音樂)。由于是低帶寬優先的,質量常會降低。
          Wave.wavWave (waveform) 格式是由 IBM 和微軟開發的。所有運行 Windows 的計算機和所有網絡瀏覽器(除了 Google Chrome)都支持它。
          WMA.wmaWMA 格式 (Windows Media Audio),質量優于 MP3,兼容大多數播放器,除了 iPod。WMA 文件可作為連續的數據流來傳輸,這使它對于網絡電臺或在線音樂很實用。
          MP3
          • .mp3
          • .mpga
          MP3 文件實際上是 MPEG 文件的聲音部分。MPEG 格式最初是由運動圖像專家組開發的。MP3 是其中最受歡迎的針對音樂的聲音格式。期待未來的軟件系統都支持它。
          HTML5 的最新標準支持 MP3, WAV, 和 Ogg 音頻格式。

          如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!


          上一篇:HTML 之簡介
          下一篇:HTML中的列表與表格
          主站蜘蛛池模板: 亚洲精品伦理熟女国产一区二区| 亚州日本乱码一区二区三区| 精品国产日韩一区三区| 精品无码国产一区二区三区AV | 日本免费一区二区三区四区五六区| 亚洲一区无码中文字幕| 天堂不卡一区二区视频在线观看 | 精品一区二区三区东京热| 八戒久久精品一区二区三区| 国产精品视频一区国模私拍| 亚洲AV噜噜一区二区三区| 久久无码人妻一区二区三区午夜| 精品亚洲一区二区| www亚洲精品少妇裸乳一区二区 | 内射少妇一区27P| AV无码精品一区二区三区| 一本久久精品一区二区| 日韩精品人妻一区二区三区四区| 色系一区二区三区四区五区| 免费无码一区二区三区| 国产伦精品一区三区视频| 国产日韩精品一区二区三区在线 | 日本在线视频一区二区三区| 国产精品无码一区二区三区在| 日韩精品无码人妻一区二区三区| 久久99精品免费一区二区| 亚洲AV噜噜一区二区三区| 国产成人无码AV一区二区在线观看| 日本精品夜色视频一区二区| 日本内射精品一区二区视频| 亚洲国产成人精品无码一区二区| 日日摸夜夜添一区| 欧美成人aaa片一区国产精品| 亚洲综合一区二区精品久久| 国产拳头交一区二区| 精品不卡一区二区| 日本精品一区二区在线播放| 国产一区二区三区在线影院| 毛片一区二区三区| 国模少妇一区二区三区| 亚洲熟妇无码一区二区三区|