最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

利用Java技術(shù)開發(fā)音樂創(chuàng)作輔助軟件

2019-05-02 19:35 作者:swiss126  | 我要投稿

? 在科學(xué)技術(shù)日益進(jìn)步的今天,各個(gè)領(lǐng)域與計(jì)算機(jī)科學(xué)技術(shù)融合發(fā)展,產(chǎn)生了一片欣欣向榮的發(fā)展景象。

利用Java面向?qū)ο缶幊碳夹g(shù)開源庫(kù)多,跨平臺(tái)移植性好以及自己對(duì)音樂知識(shí)有所了解的優(yōu)勢(shì),我想幫助無數(shù)個(gè)像我一樣的業(yè)余音樂人,所以我設(shè)計(jì)了一款音樂軟件,為喜歡音樂的人開辟一條音樂學(xué)習(xí)與創(chuàng)作的途徑。

該軟件目前使用了Java編程語(yǔ)言,JFugue開源工具包,Java Swing GUI技術(shù),MIDI技術(shù)。實(shí)現(xiàn)了MIDI編輯、解析、保存的功能。論文對(duì)所用的技術(shù)細(xì)節(jié)和開發(fā)過程進(jìn)行了詳盡的介紹。

關(guān)鍵詞:Java;MIDI;JFugue;Java Swin

The Digital Audio Workstation developed by Java

Major:?Computer science and technology Class:?computer 142? Name:?swiss126?? Instructor: uOiaH

Abstract: Today, with the progress of science and technology, the integration and development of various fields with computer science and technology has produced a flourishing development scene.

I want to help countless amateur musicians like me, using Java - oriented programming technology, open source libraries, cross platform portability and understanding of music knowledge. So I designed a music software to open up a way of music learning and creation for people who like music.

The software currently uses Java programming language, JFugue Open Source Toolkit, Java Swing GUI technology, MIDI technology. The functions of editing, parsing and saving of MIDI are realized. The technical details and development process are introduced in detail in this paper.

Keywords: Java ; MIDI; JFugue;Java Swing

第1章 項(xiàng)目分析

1.1 現(xiàn)狀

????????在科學(xué)技術(shù)日益進(jìn)步的今天,計(jì)算機(jī)科學(xué)技術(shù)是其中比較突出的一個(gè)分支,各個(gè)領(lǐng)域與計(jì)算機(jī)科學(xué)技術(shù)融合發(fā)展,產(chǎn)生了一片欣欣向榮的發(fā)展景象,移動(dòng)支付,電子商務(wù),虛擬現(xiàn)實(shí),人工智能處處可見。

????????音樂領(lǐng)域也是如此,虛擬歌手的出現(xiàn)把音樂發(fā)展推到了一個(gè)新的高度,自從VOCALOID虛擬歌手誕生以來,吸引了無數(shù)粉絲,也吸引了無數(shù)音樂人為她作曲編曲。就拿中文v家來說,從2012年洛天依出道開始,到現(xiàn)在也就短短6年,創(chuàng)造了784首殿堂曲,其中有不少膾炙人口的優(yōu)秀作品流傳在各個(gè)地方。這對(duì)于樂壇來說是不小的推動(dòng)。

????????然而業(yè)余音樂人想要進(jìn)行音樂創(chuàng)作,總會(huì)遇到各種困難,比如入門難,學(xué)習(xí)培訓(xùn)費(fèi)用高等各種困難。為此我們打算設(shè)計(jì)一款音樂軟件,為喜歡音樂的人開辟一條音樂學(xué)習(xí)與創(chuàng)作的途徑。該軟件包含音樂創(chuàng)作輔助功能,以及簡(jiǎn)易的樂理知識(shí)介紹,可以方便音樂人或者音樂愛好者學(xué)習(xí)與創(chuàng)作。

1.2 需求分析

1、該軟件主要面向業(yè)余音樂愛好者,此類人群大多數(shù)沒有專業(yè)的電腦,所以程序需要足夠簡(jiǎn)潔輕量,在各種不同的系統(tǒng)環(huán)境中都可以正常運(yùn)行。Java編程語(yǔ)言擁有面向?qū)ο缶幊?、移植性良好、安全性高、擁有并發(fā)機(jī)制、支持可視化圖形界面等優(yōu)點(diǎn),使用我選擇使用Java開發(fā)項(xiàng)目。

2、目前主流的音樂軟件大多數(shù)是外國(guó)人使用C++開發(fā),其中一些軟件體量龐大,安裝復(fù)雜,操作繁瑣,不利于業(yè)余音樂愛好者創(chuàng)作和學(xué)習(xí)。因此,項(xiàng)目需要做到足夠精簡(jiǎn),足夠好用,在符合中國(guó)用戶的操作習(xí)慣的基礎(chǔ)上實(shí)現(xiàn)國(guó)際化。

3、根據(jù)音樂人創(chuàng)作音樂需要的步驟來看,如圖 1?1所示,軟件需要包含作曲編曲(包括創(chuàng)作輔助功能)、混音混縮、讀寫文件(包括打譜,導(dǎo)入,導(dǎo)出工程文件,發(fā)布作品)等模塊。

4、需要支持主流的音樂工程文件以及特殊人群常用的工程文件,實(shí)現(xiàn)支持MIDI,VSTi等通用性強(qiáng)的技術(shù)標(biāo)準(zhǔn),這樣可以方便用戶與其他軟件用戶進(jìn)行交流與學(xué)習(xí)。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

圖 1?1 音樂人作曲編曲的流程示意圖

1.3 設(shè)計(jì)方案

根據(jù)軟件需求分析,仔細(xì)研究思考以后,決定使用以下設(shè)計(jì)方案:

1、采用Java語(yǔ)言開發(fā)項(xiàng)目,根據(jù)實(shí)際需求選擇合適的插件與開發(fā)包,減輕開發(fā)工作量。

2、界面足夠精簡(jiǎn)清爽,允許用戶設(shè)置界面的背景圖片以及系統(tǒng)顏色,語(yǔ)言包等選項(xiàng)。

3、軟件需要包含作曲編曲(包括創(chuàng)作輔助功能)、混音混縮、讀寫文件(包括打譜,導(dǎo)入,導(dǎo)出工程文件,發(fā)布作品)等模塊。采用快速原型模型與漸增模型的方法開發(fā)軟件,用戶可以隨時(shí)看到開發(fā)進(jìn)度,并且可以對(duì)軟件提出建議,這樣開發(fā)出來的軟件會(huì)更符合用戶的需求,以及使用習(xí)慣。

4、努力支持其他軟件的工程文件以及通用技術(shù)標(biāo)準(zhǔn),方便用戶交流與學(xué)習(xí)。

1.4 研發(fā)規(guī)劃

本項(xiàng)目打算分3個(gè)階段進(jìn)行研發(fā):

1、技術(shù)試驗(yàn)階段,畢設(shè)期間主要是摸索探討實(shí)現(xiàn)計(jì)算機(jī)音樂創(chuàng)作軟件需要的用到技術(shù),以及需要實(shí)現(xiàn)的技術(shù)標(biāo)準(zhǔn),盡力實(shí)現(xiàn)核心模塊,為之后進(jìn)一步開發(fā)打下扎實(shí)的基礎(chǔ)。

2、研發(fā)階段,工作1-2年期間,利用空余時(shí)間對(duì)畢設(shè)作品已實(shí)現(xiàn)的功能進(jìn)行算法優(yōu)化,未實(shí)現(xiàn)的功能繼續(xù)實(shí)現(xiàn)并且根據(jù)既定標(biāo)準(zhǔn)進(jìn)行算法優(yōu)化。

3、運(yùn)營(yíng)維護(hù)階段,項(xiàng)目開發(fā)得到可以穩(wěn)定運(yùn)行功能齊全的軟件后,打算尋求運(yùn)營(yíng)商代理運(yùn)營(yíng)軟件。期間本人將會(huì)負(fù)責(zé)軟件的技術(shù)支持及版本更新。同時(shí)利用制作軟件學(xué)到的技術(shù),進(jìn)行衍生產(chǎn)品的研發(fā)。比如說可以投入音樂教學(xué)的APP,可以提醒用戶工作學(xué)習(xí)的虛擬對(duì)象等應(yīng)用領(lǐng)域研發(fā)。

第2章 技術(shù)簡(jiǎn)介

2.1 MIDI

?????? MIDI(Musical Instrument Digital Interface)樂器數(shù)字接口,是20 世紀(jì)80 年代初為解決電聲樂器之間的通信問題而提出的。MIDI是編曲界最廣泛的音樂標(biāo)準(zhǔn)格式,它用音符的數(shù)字控制信號(hào)來記錄音樂。一首完整的MIDI音樂只有幾十KB大,而能包含數(shù)十條音樂軌道。

2.2 JFugue

???????? JFugue 使用字符串來描述音樂信息,然后將其轉(zhuǎn)換成 MIDI 指令并通過 Java Sound 接口對(duì)其進(jìn)行處理,因此他可以播放、讀取、保存MIDI文件。他的 API 非常的容易理解和使用,所以我們使用JFugue來進(jìn)行MIDI編程。

2.3 Swing

???????? Swing 是一個(gè)為Java設(shè)計(jì)的GUI工具包,他是JAVA基礎(chǔ)類的一部分。Swing提供許多比AWT更好的屏幕顯示元素。它們用純Java寫成,所以同Java本身一樣可以跨平臺(tái)運(yùn)行。

2.4 多線程

???????? 采用了多線程技術(shù)可以讓應(yīng)用程序更好地利用系統(tǒng)資源。其主要優(yōu)勢(shì)在于充分利用了CPU的空閑時(shí)間片,可以用盡可能少的時(shí)間來對(duì)用戶的要求做出響應(yīng),使得進(jìn)程的整體運(yùn)行效率得到較大提高,同時(shí)增強(qiáng)了應(yīng)用程序的靈活性。

第3章 軟件設(shè)計(jì)

3.1 軟件功能

軟件功能有作曲編曲(包括創(chuàng)作輔助功能)、混音混縮、讀寫文件(包括打譜,導(dǎo)入,導(dǎo)出工程文件,發(fā)布作品)等模塊,目前主要介紹作曲編曲模塊。

作曲編曲模塊分為工程設(shè)置,鋼琴窗,音軌編輯器,音軌設(shè)置四個(gè)子模塊構(gòu)成,主要功能就是輔助用戶完成作曲編曲的操作,必要的情況下應(yīng)該給用戶樂理知識(shí)的提示,幫助樂理基礎(chǔ)不扎實(shí)的用戶創(chuàng)作好聽的作品。

混音混縮模塊主要就是混音臺(tái)和效果器,該模塊輔助用戶完成歌曲的混音工作,得到可以播放的音頻伴奏或者歌曲試聽。

讀寫文件模塊負(fù)責(zé)數(shù)據(jù)的本地持久化,保存、備份用戶的工程文件,導(dǎo)出做好的作品,如果有可能還需幫助用戶將作品快速的發(fā)布到音樂平臺(tái)。

3.2 軟件性能

1.???????? Windows7 64位、JRE1.8、4GB內(nèi)存、500G硬盤、Intel Celeron CPU 847@ 1.10GHz

環(huán)境中流暢運(yùn)行

2.???????? 穩(wěn)定的播放、讀寫、解析、編輯MIDI文件

3.???????? 鋼琴窗能支持1000個(gè)音符的音序流暢顯示和操作

4.???????? 音軌編輯器支持32音軌編輯

3.3 軟件架構(gòu)

???????? 如圖 3?1所示,本軟件采用MVC三層架構(gòu),Model層利用JFugue實(shí)現(xiàn)了MIDI數(shù)據(jù)結(jié)構(gòu)建模,View層使用Java Swing以及Window Builder插件搭建,Ctrl層利用多線程技術(shù)控制程序運(yùn)轉(zhuǎn)。

圖 3?1 工程分層分包圖

3.3.1 View層

???????? View層基于Java Swing開發(fā),通過繼承重寫Java Swing的控件實(shí)現(xiàn)自定義控件的效果,類圖如圖 3?2所示。由于作曲編曲軟件的太小眾了,Java Swing并沒有提供相關(guān)的控件,鋼琴窗,音軌編輯器需要自己動(dòng)手編寫控件。為了實(shí)現(xiàn)這種大規(guī)模集成的控件,我決定使用Eclipse的Window Builder插件(插件界面如圖 3?3所示)進(jìn)行輔助設(shè)計(jì),設(shè)計(jì)完界面再進(jìn)行代碼優(yōu)化完善。

圖 3?2 View層 部分類圖

圖 3?3 WindowBuilder界面

View層界面由多個(gè)內(nèi)部窗口和控件構(gòu)成,采用異步刷新的方式可以減少系統(tǒng)資源占用,提高應(yīng)用程序的效率,界面的刷新機(jī)制如圖 3?4所示

圖 3?4界面刷新機(jī)制

?

3.3.2 Control層

1.???????? MIDI解析

???????? 軟件需要加載MIDI中的數(shù)據(jù),并且把他們顯示出來,這就涉及到了MIDI解析的問題。本人在JFugue的API基礎(chǔ)之上設(shè)計(jì)了一個(gè)MIDI解析方法,如圖 3?5所示。

圖 3?5 MIDI解析

2.???????? 多線程播放

???????? 如圖 3?6所示,在MIDI底層播放機(jī)制中,MIDI序列被分成了多個(gè)音軌,這些音軌有多線程控制,在對(duì)應(yīng)的時(shí)間發(fā)送短信息給音序器,音序器根據(jù)發(fā)送的信息進(jìn)行對(duì)應(yīng)的處理,比如播放音符,改變音色,切換MIDI通道等,這樣的機(jī)制需要考慮到線程同步的問題。

圖 3?6 MIDI播放機(jī)制

???????? 在JFugue的基礎(chǔ)上,我設(shè)計(jì)了一套多線程播放機(jī)制,讓播放器更加能夠適合我們的需求。

這套機(jī)制的播放及其狀態(tài)轉(zhuǎn)換如圖 3?7、圖 3?8、圖 3?9所示。

圖 3?7 SRealtimePlayer播放機(jī)制

圖 3?8 ProjectPlayer播放機(jī)制

圖 3?9 播放狀態(tài)轉(zhuǎn)換機(jī)制

3.3.3 Model層

???????? Model層根據(jù)MIDI文件的數(shù)據(jù)結(jié)構(gòu),在JFugue的基礎(chǔ)上設(shè)計(jì)了這套模型。模型中包含MidiDictionary、Note、Sequence、Track這四個(gè)類。為了與JFugue的類相區(qū)別,我給Note、Sequence、Track加上了前綴My。MyNote是音符類,表示樂曲中的一個(gè)音符。MySequence是音序類,表示樂曲中的一個(gè)片段。MyTrack是音軌類,表示樂曲中的一個(gè)音軌。其關(guān)系如圖 3?10所示,數(shù)據(jù)字典如表 3?1、表 3?2、表 3?3所示。

表 3?1 MyTrack 數(shù)據(jù)字典

表 3?2 MySequence 數(shù)據(jù)字典

表 3?3MyNote 數(shù)據(jù)字典


第4章 功能實(shí)現(xiàn)

4.1 作曲編曲

???????? 這個(gè)模塊是整個(gè)軟件的核心模塊,軟件中的所有功能都需要圍繞他們運(yùn)轉(zhuǎn)。

???????? 目前界面主體采用在JPanel上繪制圖形圖像的方法畫出鋼琴窗與音軌編輯器的網(wǎng)格,用JTextField控件模擬音符和音序,用滾動(dòng)條控制界面縮放移動(dòng),用復(fù)位按鈕實(shí)現(xiàn)界面快速還原原始比例。

4.1.1 鋼琴窗

???????? 鋼琴窗界面如圖 4?1所示,上面可以很清楚的看到一個(gè)音序內(nèi)包含的音符,并且可以用工具欄中的工具對(duì)他們進(jìn)行修改。

圖 4?1 鋼琴窗界面設(shè)計(jì)

鋼琴窗由PianoRollFrame、PianoPanel、NoteField、ParameterCanvas、PianoCanvas等五個(gè)類共同實(shí)現(xiàn),

其中PianoRollFrame是模塊的主類;PianoPanel是鋼琴窗左側(cè)的鋼琴鍵盤,可以顯示音高,方便用戶操作;NoteField是實(shí)現(xiàn)音符控件的類,支持音符的創(chuàng)建,刪除,修改,移動(dòng)。ParameterCanvas是參數(shù)編輯面板的控件類,PianoCanvas是音符編輯面板的控件類。

?

如表 4?1所示,PianoRollFrame類包含以下變量和常量:

表 4?1 PianoRollFrame類包含的變量和常量

4.1.2 界面繪制方法

???????? PianoPanel直接畫出準(zhǔn)備好的鋼琴窗圖片,并且在上面標(biāo)上音名。ParameterCanvas和PianoCanvas重載paintComponent方法,用java.awt.Graphics自帶的繪圖方法繪制線條,給線條的顏色加點(diǎn)透明效果,可以讓畫出來的網(wǎng)格更好看。

用PianoRollFrame的變量dX,dY,dW,dH控制PianoPanel、ParameterCanvas、PianoCanvas的繪制線條位置,長(zhǎng)度以及顏色,可以實(shí)現(xiàn)好看的效果。

控制方法如下:

???????? 用grade控制線條的級(jí)別,不同級(jí)別的線條粗細(xì)不同,alpha控制線條的顏色的透明度,當(dāng)縱軸拉長(zhǎng)的時(shí)候,橫線會(huì)變粗,縱軸拉短的時(shí)候,橫線變細(xì),用dX,dY,dW,dH根據(jù)平面直角坐標(biāo)系圖形的縮放就可以計(jì)算出線條應(yīng)該落在的位置。最后用for循環(huán)可以把橫線畫滿鋼琴窗。

縱線的繪圖方法類似,但是由于不同縮放比例下顯示縱軸的情況不一樣,當(dāng)橫軸的比例縮得太小的時(shí)候,一些縱線會(huì)隱藏起來,所以需要加個(gè)判斷是否繪制出縱線。

繪制方法如下:

4.1.3 界面刷新機(jī)制

???????? 拖動(dòng)滾動(dòng)條控件改變dX,dY,dH,dW四個(gè)變量之一時(shí)或者添加刪除修改音符時(shí),鋼琴窗會(huì)自動(dòng)刷新,加載需要顯示的音符,并把編輯過的音符保存到對(duì)應(yīng)的音序中。

刷新方法如下:

4.1.4 參數(shù)編輯器

???????? 參數(shù)編輯器是鋼琴窗底部的一個(gè)控件,用于編輯音符參數(shù),如圖 4?2所示。通過JFugue包提供的方法可以對(duì)MIDI的參數(shù)控制器進(jìn)行設(shè)置,在JFugue中,用Token記號(hào)“:PW”設(shè)置彎音輪,記號(hào)“:CE”設(shè)置參數(shù)編輯器,“a”記號(hào)設(shè)置音符初始力度,“d”記號(hào)設(shè)置音符結(jié)束力度,通過控制以上參數(shù)可以做出美妙動(dòng)聽的音樂。比如JFugue Pattern “:CE(93,127) :CE(8,127) E3wa100d0”表示 設(shè)置控制器第93個(gè)參數(shù),值為127,設(shè)置控制器第8個(gè)參數(shù),值為127,播放音符E3,初始力度100,結(jié)束力度0。再如“:PW(0,64)”表示設(shè)置彎音輪,值為高位是0,低位是64。

圖 4?2 參數(shù)編輯器

4.1.5 音軌編輯器

???????? 音軌編輯器的界面如圖 4?3所示,其工作原理與鋼琴窗類似,就是通過繪制線條,改變控件位置實(shí)現(xiàn)特定效果。音軌編輯器由SequenceField、TrackCanvas、TrackFrame、TrackMsgEditor、TrackMsgPanel等五個(gè)類共同實(shí)現(xiàn)。

圖 4?3音軌編輯器界面設(shè)計(jì)

如表 4?2所示,TrackFrame類包含以下變量和常量:

表 4?2 TrackFrame類包含的變量和常量

控件的繪圖機(jī)制與刷新機(jī)制和鋼琴窗類似,因此不再贅述。

4.2 工程設(shè)置

???????? 音樂作品不是千篇一律的,節(jié)拍,調(diào)式調(diào)性,曲速,樂器都會(huì)根據(jù)音樂人的需要發(fā)生改變。這個(gè)模塊就是通過MIDI接口實(shí)現(xiàn)幫助音樂人設(shè)置音樂項(xiàng)目的功能。如圖 4?4圖 4?5所示,用戶可以根據(jù)自己需要設(shè)置作品的曲速,節(jié)拍,MIDI端口號(hào),演奏樂器,歌曲長(zhǎng)度。

圖 4?4 工程設(shè)置

圖 4?5 音軌設(shè)置

???????? 設(shè)置模塊由ProjectEditor和TrackMsgEditor、Main這三個(gè)類構(gòu)成,其中TrackMsgEditor從屬于音軌編輯器模塊。在MIDI標(biāo)準(zhǔn)中,有16個(gè)MIDI通道(其中第10個(gè)通道是鼓映射通道,用來演奏鼓樂器),128種樂器,和一個(gè)GM鼓映射。用JFugue包可以輕松的對(duì)他們進(jìn)行設(shè)置,用JFugue的Token記號(hào)I,可以設(shè)置樂器,比如記號(hào)“I0”就是選擇使用MIDI標(biāo)準(zhǔn)中的0號(hào)樂器——鋼琴。另外可以用記號(hào)V來設(shè)置MIDI通道,“V0”表示0號(hào)通道,“V1”表示1號(hào)通道,依次類推直到“V15”表示15號(hào)通道。其中“V9”是鼓映射通道,用來演奏鼓樂器。用TIME記號(hào)可以設(shè)置節(jié)拍,T記號(hào)設(shè)置曲速(單位是BPM),Key記號(hào)設(shè)置調(diào)性,例如JFugue Pattern “TIME:4/4 KEY:Gmaj T100”表示歌曲的節(jié)拍是4/4拍,調(diào)性是G大調(diào),曲速是100BPM。音軌顏色的設(shè)置主要是為了方便音樂人區(qū)分不同音軌,提高作曲編曲的效率。

4.3 文件讀寫

???????? 在音樂編輯軟件中,文件讀寫是實(shí)現(xiàn)數(shù)據(jù)本地持久化的主流方法。音樂人可以通過保存文件的方式把自己的作品永遠(yuǎn)的保存起來。如圖 4?6所示,本系統(tǒng)目前支持MIDI標(biāo)準(zhǔn)格式文件(后綴為.mid)未來將會(huì)支持其他的主流文件格式以及特殊人群通用的文件格式。

圖 4?6 文件讀寫模塊

4.3.1 打開MIDI文件

???????? 在JFugue中,打開文件只需要調(diào)用方法MidiFileManager.loadPatternFromMidi(File file)即可實(shí)現(xiàn)最基本的打開操作。然而軟件需要加載MIDI中的數(shù)據(jù),并且把他們顯示出來,這就涉及到了MIDI解析的問題。目前JFugue中提供的MIDI解析方法不夠完善,pattern.getTokens()只能得到一些拆分過的Token記號(hào),光靠這些記號(hào)無法把打開的MIDI文件顯示到界面上。于是本人寫了一個(gè)解析Token的方法來實(shí)現(xiàn)進(jìn)一步的MIDI解析。解析方法如下:

public void LoadMidi(Pattern pattern){//解析讀取到的midi,并在程序中創(chuàng)建相應(yīng)的音序,音軌,音符

???????? System.out.println("解析MIDI:"+pattern);

???????? TrackFrame.Reset();

???????? /**

???????? ?* 解析到的記號(hào)列

???????? ?*/

???????? List<Token> ? tokens=pattern.getTokens();

???????? /**

???????? ?* 新建音序列

???????? ?*/

???????? List<MySequence> ? sequenceList=new ArrayList<MySequence>();

???????? /**

???????? ?* 新建音序

???????? ?*/

???????? MySequence ? sequence = new MySequence(0,0, Main.length,(int) (TrackFrame.DefaultHeight), ? "未命名音序");

???????? /**

???????? ?* 新建音符

???????? ?*/

???????? MyNote ? note=new MyNote(0, 0, 0, 0, "", 0, 0, 0);

???????? /**

???????? ?* 音符計(jì)時(shí)器,音軌計(jì)時(shí)器

???????? ?*/

???????? double ? NoteTimer=0d,TrackTimer=0d,Length=0d;

???????? /**

???????? ?* 控制參數(shù)(CE)集

???????? ?*/

???????? Map<Integer,String> ? ParameterList=new HashMap<Integer,String>();

???????? boolean ? IsFirstNote=true,IsJump=false;

???????? int ? sequencecount=0,midiport=0,instrument=0;

???????? for(Token ? t:tokens){

?????????????????? switch(t.getType()){

?????????????????? case ? VOICE://端口號(hào)

??????????????????????????? if(sequence.getPattern().length()>0){//再次遇到V記號(hào)的時(shí)候,上一個(gè)音軌的數(shù)據(jù)解析完成

???????????????????????????????????? sequencecount++;

???????????????????????????????????? sequence.setTxt(MidiDictionary.InstrumentType.get(sequence.getInstrument()));

???????????????????????????????????? sequence.set_width((int) ? NoteTimer*Main.SNote*4-sequence.get_x()+1);

???????????????????????????????????? if(sequence.getNoteList().size()>0){

?????????????????????????????????????????????? if(sequence.getMIDIPort()==9)sequence.setTxt("GM鼓音色");

?????????????????????????????????????????????? System.out.println(sequence+"\n"+sequence.getPattern());

?????????????????????????????????????????????? sequenceList.add(sequence);

???????????????????????????????????? }

???????????????????????????????????? sequence.setPattern("");

???????????????????????????????????? if(NoteTimer>Length)Length=NoteTimer;

???????????????????????????????????? sequence ? = new MySequence(0,0, Main.length,(int) (TrackFrame.DefaultHeight), "未命名音序");

???????????????????????????????????? sequence.setMIDIPort(midiport);

???????????????????????????????????? sequence.setInstrument(instrument);

???????????????????????????????????? IsFirstNote=true;

???????????????????????????????????? NoteTimer=0d;

??????????????????????????? }

??????????????????????????? if(sequencecount<32)sequence.set_y(sequencecount);

??????????????????????????? sequence.set_x((int) ? (TrackTimer*Main.SNote*4));

??????????????????????????? sequence.set_height(TrackFrame.DefaultHeight);

??????????????????????????? midiport=Integer.parseInt(t.getPattern().toString().substring(1));

??????????????????????????? break;

?????????????????? case ? LAYER:

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? break;

?????????????????? case ? INSTRUMENT://樂器號(hào)

??????????????????????????? if(sequence.getPattern().length()>0){//再次遇到I記號(hào)的時(shí)候,說明上一個(gè)音軌的數(shù)據(jù)解析完成

???????????????????????????????????? sequence.setTxt(MidiDictionary.InstrumentType.get(sequence.getInstrument()));

???????????????????????????????????? sequence.set_width((int) ? NoteTimer*Main.SNote*4-sequence.get_x()+1);

???????????????????????????????????? if(sequence.getNoteList().size()>0){

?????????????????????????????????????????????? if(sequence.getMIDIPort()==9)sequence.setTxt("GM鼓音色");

?????????????????????????????????????????????? sequencecount++;

?????????????????????????????????????????????? sequence.set_y(sequencecount);

?????????????????????????????????????????????? sequenceList.add(sequence);

???????????????????????????????????? }

???????????????????????????????????? sequence.setPattern("");

???????????????????????????????????? if(NoteTimer>Length)Length=NoteTimer;

??????????????????????????? ???????? sequence = new MySequence(0,0, ? Main.length,(int) (TrackFrame.DefaultHeight), "未命名音序");

???????????????????????????????????? sequence.setMIDIPort(midiport);

???????????????????????????????????? sequence.setInstrument(instrument);

???????????????????????????????????? IsFirstNote=true;

???????????????????????????????????? NoteTimer=0d;

??????????????????????????? }

??????????????????????????? sequence.set_x((int) ? (TrackTimer*Main.SNote*4));

??????????????????????????? sequence.set_height(TrackFrame.DefaultHeight);

??????????????????????????? instrument=Integer.parseInt(t.getPattern().toString().substring(1));

??????????????????????????? break;

?????????????????? case ? TEMPO://曲速

??????????????????????????? Main.BPM=Double.parseDouble(t.getPattern().toString().substring(1));

??????????????????????????? break;

?????????????????? case ? KEY_SIGNATURE://調(diào)

??????????????????????????? //System.out.println("Pattern:"+t.getPattern()+"\tType:"+t.getType());

??????????????????????????? break;

?????????????????? case ? TIME_SIGNATURE://節(jié)拍

??????????????????????????? Main.Meter=Integer.parseInt(t.getPattern().toString().split("/")[0].substring(5));

??????????????????????????? Main.Part=Integer.parseInt(t.getPattern().toString().split("/")[1]);

??????????????????????????? break;

?????????????????? case ? BAR_LINE:

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? break;

?????????????????? case ? TRACK_TIME_BOOKMARK://音符開始時(shí)間

??????????????????????????? TrackTimer=Double.parseDouble(t.getPattern().toString().substring(1));

??????????????????????????? IsJump=true;

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? break;

?????????????????? case ? TRACK_TIME_BOOKMARK_REQUESTED:

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? break;

?????????????????? case ? LYRIC:

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? break;

?????????????????? case ? MARKER:

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? break;

?????????????????? case ? FUNCTION:

??????????????????????????? if(t.getPattern().toString().contains("CE"))

???????????????????????????????????? ParameterList.put(Integer.parseInt(t.getPattern().toString().split(",")[0].substring(4)),t.get

Pattern().toString().split(",")[1].substring(0, ? t.getPattern().toString().split(",")[1].length()-1));

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? break;

?????????????????? case ? NOTE:

??????????????????????????? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

??????????????????????????? Note ? jnote = null;

??????????????????????????? try{

???????????????????????????????????? jnote=new ? Note(t.getPattern().toString());

??????????????????????????? }catch(Exception ? e){//如果無法識(shí)別,很可能是鼓

???????????????????????????????????? sequence.setMIDIPort(9);

???????????????????????????????????? sequence.setSequenceType(App.Rhythm);

???????????????????????????????????? sequence.setTxt("GM鼓音色");

???????????????????????????????????? String ? temp=t.getPattern().toString();

???????????????????????????????????? temp=temp.replace("["+temp.split("]")[0].substring(1)+"]",MidiDictionary.RhythmToKey

.get(temp.split("]")[0].substring(1)));

???????????????????????????????????? jnote=new ? Note(temp);

?????????????????? ???????? }

??????????????????????????? if(IsJump){

???????????????????????????????????? note.set_x((int) ? (TrackTimer*Main.SNote*4));

???????????????????????????????????? NoteTimer=TrackTimer+jnote.getDuration();

??????????????????????????? }else{

???????????????????????????????????? note.set_x((int) ? (NoteTimer*Main.SNote*4));

???????????????????????????????????? NoteTimer+=jnote.getDuration();

??????????????????????????? }

??????????????????????????? note.set_y(119-jnote.getValue());

??????????????????????????? note.set_width((int) ? (jnote.getDuration()*Main.SNote*4));

??????????????????????????? note.set_height((int) ? PianoRollFrame.DefaultKey);

??????????????????????????? note.setParameterList(ParameterList);

??????????????????????????? if(jnote.getValue()>0){

???????????????????????????????????? if(IsFirstNote)sequence.set_x(note.get_x());

???????????????????????????????????? sequence.updateNote(note);

???????? ?????????????????? }

??????????????????????????? IsFirstNote=false;

??????????????????????????? IsJump=false;

??????????????????????????? note=new ? MyNote(0, 0, 0, 0, "", 0, 0, 0);

??????????????????????????? ParameterList=new ? HashMap<Integer,String>();

??????????????????????????? TrackTimer=0d;

??????????????????????????? break;

??????? ? default:

??????? ? ?? sequence.setPattern(sequence.getPattern()+" ? "+t.getPattern());

?? ??????? break;

?????????????????? }

???????? }

???????? sequencecount++;

???????? sequence.setTxt(MidiDictionary.InstrumentType.get(sequence.getInstrument()));

???????? sequence.set_width((int) ? NoteTimer*Main.SNote*4-sequence.get_x()+1);

???????? if(sequence.getNoteList().size()>0)sequenceList.add(sequence);

???????? sequence.setPattern("");

???????? if(NoteTimer>Length)Length=NoteTimer;

???????? NoteTimer=0d;

???????? List<SequenceField> ? sequenceFieldList=new ArrayList<SequenceField>();

???????? for(MySequence ? s:sequenceList){

?????????????????? SequenceField ? sequenceField=new SequenceField(s.getTxt(),s.get_x(),s.get_y(), ? s.get_width(), s.get_

height());

?????????????????? sequenceField.sequence=s;

?????????????????? TrackFrame.trackmsgpanel[s.get_y()].track.setInstrument(s.getInstrument());

?????????????????? TrackFrame.trackmsgpanel[s.get_y()].track.setMIDIPort(s.getMIDIPort());

?????????????????? TrackFrame.trackmsgpanel[s.get_y()].track.setTrackType(s.getSequenceType());

?????????????????? TrackFrame.trackmsgpanel[s.get_y()].track.setTxt(s.getTxt());

?????????????????? TrackFrame.trackmsgpanel[s.get_y()].Background=App.FieldColor;

?????????????????? sequenceFieldList.add(sequenceField);

?????????????????? //System.out.println(s);

???????? }

???????? Main.length=(int)Length+1;

???????? PianoRollFrame.Blank.setText(Main.Meter+"/"+Main.Part+"拍 "+Main.length+"節(jié) BPM="+Main.BP

M);

???????? TrackFrame.Blank.setText(Main.Meter+"/"+Main.Part+"拍 "+Main.length+"節(jié) BPM="+Main.BPM);

???????? TrackFrame.sequence=sequenceFieldList;

???????? MainFrame.desktopPane.repaint();

???????? MainFrame.InternalFrame.get(App.$Track).setVisible(true);

???????? MainFrame.Stbtn[2][App.$Track].setSelected(true);

???????? TrackFrame.Repaint();

}

4.3.2 保存MIDI文件

???????? JFugue自帶的保存文件方法是MidiFileManager.savePatternToMidi(PatternProducer patternProducer, File file) throws IOException,這個(gè)方法可以把一段JFugue Pattern保存成MIDI文件。所以我要做的就是把軟件中的數(shù)據(jù)轉(zhuǎn)換成JFugue Pattern,然后再調(diào)用這個(gè)方法保存MIDI文件。

???????? 根據(jù)JFugue的API可以知道不同記號(hào)的意思,于是我寫了一些Model模型,他們有得到JFugue Pattern的方法。為了和JFugue的模型區(qū)分開來,我自己的模型類名前面加了前綴“My”。

MyNote的獲取Pattern方法如下:

/**

?* 得到音符的JFugue Pattern

?* 示例:@1.0625 :CE(4,54) :CE(2,51) :CE(33,39) :CE(0,86) C#4hs

?*/

public String getPattern(){

???????? pattern=get_xString()+" ? "+getParameterString()+" "+getNote()+getLength();

???????? return ? pattern;

}

/**

?* 得到音符的長(zhǎng)度

?*/

public String getLength(){

???????? String ? temp="";

???????? int ? length=_width;

???????? Stack<Boolean> ? s=new Stack<Boolean>();

???????? do{

?????????????????? s.push(length%2>0);

?????????????????? length=length/2;

???????? }while(length>0 ? && s.size()<5);

???????? for(int ? i=0;i<length;i++)

???????? {

?????????????????? temp+=lengthNote[0];

???????? }

???????? int ? part=s.size();

???????? for(int ? i=0;i<part;i++)

???????? {

?????????????????? if(s.pop())temp+=lengthNote[lengthNote.length-part+i];

???????? }

???????? return ? temp;

}

/**

?* 得到音符開始播放的時(shí)間

?*/

public String get_xString(){

???????? return ? "@"+_x/32.0;

}

/**

?* 得到音符的參數(shù)

?*/

public String getParameterString(){

???????? String ? temp="";

???????? Set<Integer> ? keySet=ParameterList.keySet();

???????? for(int ? key:keySet)

???????? {

?????????????????? temp+=" ? :CE("+key+","+ParameterList.get(key)+") ";

???????? }

???????? return ? temp;

}

/**

?* 得到音高

?*/

public String getNote(){

???????? switch(_y%12)

???????? {

?????????????????? case ? 0:return "B"+(9-_y/12);

?????????????????? case ? 1:return "A#"+(9-_y/12);

?????????????????? case ? 2:return "A"+(9-_y/12);

?????????????????? case ? 3:return "G#"+(9-_y/12);

?????????????????? case ? 4:return "G"+(9-_y/12);

?????????????????? case ? 5:return "F#"+(9-_y/12);

?????????????????? case ? 6:return "F"+(9-_y/12);

?????????????????? case ? 7:return "E"+(9-_y/12);

?????????????????? case ? 8:return "D#"+(9-_y/12);

?????????????????? case ? 9:return "D"+(9-_y/12);

?????????????????? case ? 10:return "C#"+(9-_y/12);

?????????????????? case ? 11:return "C"+(9-_y/12);

???????? }

???????? return ? null;

}

???????? MySequence和MyTrack獲取Pattern的方法只需要把里面包含對(duì)象的Pattern拼接起來即可,得到所有Pattern以后通過JFugue自帶的輸出方法就能把Pattern寫到MIDI文件上面。

4.4 走帶

???????? 播放模塊主要由走帶面板和實(shí)時(shí)播放器構(gòu)成。通過多線程機(jī)制,可以實(shí)現(xiàn)播放的時(shí)候指針跟著移動(dòng),同時(shí)不影響用戶繼續(xù)操作軟件。如圖 4?7圖 4?8所示,走帶面板上有播放,暫停,停止,循環(huán)按鈕,播放指針和一個(gè)時(shí)間顯示器構(gòu)成,點(diǎn)擊時(shí)間顯示器可以切換顯示模式。目前有兩種顯示模式:一種是時(shí)間模式,就是顯示當(dāng)前播放的時(shí)間(分:秒、毫秒),另一種是節(jié)拍模式,顯示當(dāng)前播放的位置是第幾節(jié)第幾拍。

圖 4?7 走帶面板與播放指針(時(shí)間模式)

圖 4?8走帶面板與播放指針(節(jié)拍模式)

4.4.1 多線程播放機(jī)制

???????? JFugue已經(jīng)把MIDI底層的播放機(jī)制封裝成了兩種播放器,一種是Player,就是普通的MIDI播放器,只支持最基本的播放功能。另一種是RealtimePlayer,可以根據(jù)自己的需要發(fā)送MIDI信息給他,進(jìn)行實(shí)時(shí)的播放控制。這里我們用到的是RealtimePlayer。

???????? 對(duì)JFugue的Player進(jìn)行進(jìn)一步的封裝,讓他更加適合于音樂輔助軟件。封裝后的SMidiPlayer和SRealtimePlayer具有更好的多線程控制,可以防止播放時(shí)主程序不能響應(yīng)用戶操作的情況發(fā)生。

SMidiPlayer的多線程機(jī)制很簡(jiǎn)單,就是另開一個(gè)線程進(jìn)行播放操作。SRealtimePlayer的多線程處理機(jī)制考慮到MIDI信號(hào)可能會(huì)太多,一下子無法及時(shí)處理的問題,設(shè)計(jì)了堆棧暫時(shí)儲(chǔ)存未播放的音符。播放線程每播放一個(gè)音符就彈出一個(gè)音符,直到播放完成為止。具體實(shí)現(xiàn)代碼如下:

???????? ProjectPlayer類實(shí)現(xiàn)了對(duì)SRealtimePlayer的時(shí)序控制,從而實(shí)現(xiàn)走帶功能。具體控制代碼如下所示:

4.4.2 狀態(tài)控制機(jī)制

???????? 播放狀態(tài)有播放,暫停,停止,循環(huán)四種,他的控制狀態(tài)方法如下:

??

第5章 系統(tǒng)測(cè)試

5.1 測(cè)試方案設(shè)計(jì)

???????? 在軟件開發(fā)過程中,為了保障編碼完成的部分模塊可以正常運(yùn)行,需要進(jìn)行模塊測(cè)試。我設(shè)計(jì)了一些測(cè)試方法來測(cè)試軟件中已經(jīng)完成的模塊運(yùn)行情況是否符合預(yù)期,從而驗(yàn)證這些模塊是否能正常運(yùn)行,是否有必要對(duì)他們重新編碼。為了達(dá)到良好的測(cè)試效果,擬定的測(cè)試要求如下:

1.???????? 測(cè)試環(huán)境與大多數(shù)用戶系統(tǒng)環(huán)境相似:Windows7 64位、JRE1.8、4GB內(nèi)存、500G硬盤、Intel Celeron CPU 847@ 1.10GHz

2.???????? 測(cè)試內(nèi)容包含所有已經(jīng)編碼完成的模塊功能以及可能出現(xiàn)的Bug和錯(cuò)誤。

3.???????? 測(cè)試報(bào)告盡可能的詳細(xì),測(cè)試完成后可以對(duì)發(fā)現(xiàn)的問題進(jìn)行匯總分析。

5.2 測(cè)試用例設(shè)計(jì)

???????? 根據(jù)軟件的功能及模塊,劃分了以下幾個(gè)測(cè)試點(diǎn),分別是:鋼琴窗,音軌編輯器,走帶面板,文件讀模塊。采用黑盒測(cè)試策略,根據(jù)可能出現(xiàn)的錯(cuò)誤準(zhǔn)備測(cè)試用例。在鋼琴窗模塊測(cè)試中進(jìn)行編輯音符、界面刷新、發(fā)音預(yù)覽測(cè)試,音軌編輯器中進(jìn)行編輯音軌、編輯音序、打開鋼琴窗的測(cè)試,走帶面板中進(jìn)行播放狀態(tài)的測(cè)試,文件讀寫模塊中進(jìn)行新建、打開、保存MIDI文件的測(cè)試,測(cè)試用例及測(cè)試情況如表 5?1所示。

表 5?1 測(cè)試用例及測(cè)試情況

第6章 結(jié)論與展望

???????? 通過這次項(xiàng)目,我學(xué)會(huì)了如何開發(fā)大規(guī)模的項(xiàng)目,以及如何查找資料,如何解決問題,如何利用他人寫好的Jar包和API文檔,實(shí)例程序進(jìn)行項(xiàng)目開發(fā)。另外我對(duì)MIDI的文件格式,運(yùn)行機(jī)制,技術(shù)標(biāo)準(zhǔn)也有所學(xué)習(xí)理解,為進(jìn)一步開發(fā)和完善項(xiàng)目打下了扎實(shí)的技術(shù)基礎(chǔ)和理論基礎(chǔ)。

???????? 進(jìn)一步的開發(fā)計(jì)劃中我將會(huì)利用空余時(shí)間對(duì)畢設(shè)作品已實(shí)現(xiàn)的功能進(jìn)行算法優(yōu)化,未實(shí)現(xiàn)的功能繼續(xù)實(shí)現(xiàn)并且根據(jù)既定標(biāo)準(zhǔn)進(jìn)行算法優(yōu)化,努力把混音模塊、歌聲合成模塊和作曲編曲輔助模塊盡早完成。

參考文獻(xiàn):

[1] 戴歆. Java Swing程序開發(fā).精通Java Swing程序設(shè)計(jì)[M]. 武漢商業(yè)服務(wù)學(xué)院信息工程系 湖北武漢430056

[2] 楊軍. MIDI消息和標(biāo)準(zhǔn)MIDI文件格式剖析及應(yīng)用. 武漢商業(yè)服務(wù)學(xué)院基礎(chǔ)課部

[3] 電子歌聲合成軟件VOCALOID應(yīng)用探討[J]. 楊心祎.音樂時(shí)空. 2015(06)

[4] 多媒體技術(shù)基礎(chǔ)[M]. 清華大學(xué)出版社 , 林福宗編著, 2000

[5] JFugue-Java API for music programming[J]. Koelle D.

[6] The Complete Guide To JFugue-v1[J]. David Koelle

[7] 牛育謙. VSTi陜北嗩吶插件的設(shè)計(jì)與實(shí)現(xiàn)[D]. 電子科技大學(xué), 2015.

[8] Gunadi M K, Eng L M, Wijaya H K. MIDI CompoSITion Tools dengan menggunakan JFugue Java API[J].

[9] 田梅, 劉瑤, 周冰穎,. 結(jié)合KinectMIDI的和聲輔助訓(xùn)練系統(tǒng)[J]. 計(jì)算機(jī)應(yīng)用與軟件, 2015(8):68-71.

[10] Newmarch J. MIDI Java Sound[M] Linux Sound Programming. Apress, 2017.

[11] JFugue[M]. Betascript Publishing, 2010.

[12] 洛伊. Java Swing[M]. 清華大學(xué)出版社, 2004.


??

附? 錄

附錄A? MIDI參數(shù)控制

附表 1 MIDI中部分參數(shù)控制

附錄B? MIDI標(biāo)準(zhǔn)音色(略)

附表 2 MIDI標(biāo)準(zhǔn)音色

附錄C? MIDI標(biāo)準(zhǔn)鼓映射

附表 3 MIDI標(biāo)準(zhǔn)鼓映射

附表 4相關(guān)詞匯中英文對(duì)照


利用Java技術(shù)開發(fā)音樂創(chuàng)作輔助軟件的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
修文县| 建平县| 岳西县| 安图县| 绥化市| 德清县| 邳州市| 宣威市| 新蔡县| 安阳市| 龙门县| 安福县| 色达县| 根河市| 潜山县| 桃园县| 乐昌市| 武冈市| 镇雄县| 习水县| 景德镇市| 雅安市| 阳新县| 横峰县| 盘山县| 惠水县| 建宁县| 大埔区| 合肥市| 湄潭县| 阳泉市| 鄂托克前旗| 出国| 隆化县| 北碚区| 开封市| 石渠县| 城市| 维西| 新泰市| 若尔盖县|