你好,今天,我們會繼續我們的音樂之旅。
動機
上週我們展示了異星工廠太空時代音樂(FFF-406)的一般方法。我們還提到,我們有一些新技術不僅可以覆蓋這5個小時的音樂,而且還可以超越它們。這種自動創作音樂的方式是我很久以前就開始嘗試的,在異星工廠之前。我在隨機的低音部分上演奏了很多隨機的旋律,隨機的節奏基礎,都是用動作腳本編程的(是的,很舊)。結果相當激烈,但從來沒有好到足以認為它們是完成的曲目。當《法托里奧太空時代》五個小時的配樂項目開始時,我立刻想到了那些舊的實驗。現在有了彼得作曲和多尼昂編程,事情看起來不一樣了。我只是敢走這條路。現在我確信這是一個好決定。
可變的音樂曲目
每次選擇這些曲目時,它們的播放方式都不同,它們是一種程序生成的音樂。但是我們不想太瘋狂地隨機化,可變曲目更像是單個曲目的一組變體(不需要全部錄製)。這些曲目取代了在主曲目之間播放的插曲(除非你在隱藏的設置中尋找)。目標是在遊戲中花費數十或數百個小時後提供一些多樣化的音樂,常規音樂仍然是主要焦點,也是大部分配樂。可變音樂曲目在原型中定義,完全可供改裝者使用。這些是用於定義可變曲目的組件:
⚙ 樣本
這些是最小的積木。它們是根據其他規則播放的單獨音樂片段。樣本一個接一個地播放,因此當一個樣本完成時,播放將與下一個樣本無縫地繼續。
⚙ 圖層
樣本被分組到圖層中。圖層決定了單個樣本如何組合在一起。它可以像隨機選擇樣本一樣簡單,洗牌所有可用的樣本,這樣每個樣本只能播放一次,或者根據當前在不同圖層中播放的樣本來選擇樣本可能更復雜。圖層還可以包含樣本以特定方式重疊的子層。圖層內的進一步變化可以使用許多屬性來完成,定義延遲開始、洗牌圖層的重複次數和重複之間的暫停次數、重疊子層的偏移量等。這些屬性要麼來自圖層本身,要麼來自軌道的當前狀態。
圖層及其樣本的播放與每個軌道為自己定義的最小時間單位對齊,從而創建一種時間網格。
圖層的組成方式是變化的主要來源。
⚙ 節
部分是層的集合。軌道中可以有一個部分或多個部分。使用哪個部分由軌道狀態決定。此外,部分可以重疊。當只有一個部分時,它可以重疊自己。最後,一個部分可以包含一個間奏曲,作為普通音樂播放,提供了一個混合軌道的選項:部分可變,部分靜態。
⚙ 狀態
狀態和狀態之間的轉換是定義變量軌道如何組成的高級方法。他們選擇應該播放哪個部分以及它是否應該與前一個部分重疊,他們定義了許多應用於當前部分層的層屬性。
狀態之間的轉換可以基於經過的時間,也可以與特定的圖層完成相關聯。可以用不同的權重定義多個可能的下一個狀態,因此某些轉換比其他轉換更有可能。下一個狀態候選可以定義額外的條件,這些條件必須滿足才能考慮轉換的狀態。例如,可以將轉換設置為僅在下一個狀態選擇時特定樣本正在特定圖層中播放時才發生。
現在我們知道了可變軌道是由什麼組成的,讓我們看幾個軌道是如何組成的例子。請理解這些示例是一個技術演示,仍在進行中。一些細節可能會改變。音樂本身並不代表遊戲發佈時的內容,這些樣本很舊,是為了說明目的而製作的!第一個例子是包含三個部分的曲目。每個部分有三層,低音層由兩個子層組成,中間層也由兩個子層組成,旋律層和中間音。狀態之間的轉換基於旋律層的整理。
這首歌的一個實例聽起來可是這樣:
星期五報道#407 - 自動化配樂 (音頻#1)
圖片僅供參考,與錄音無關。這是一個實際的時間表:
0:01:曲目在開始狀態下開始,第二部分是隨機選擇的。
0:01:低音層開始。
0:12:中音層開始。
0:17:旋律層開始。
0:44:旋律層完成第一次重複,在第二次重複之前排按暫停。
0:51:旋律層開始第二次重複。
1:19:旋律層完成第二次也是最後一次的重複,在完成播放之前排按暫停。
1:23:旋律層播放結束,觸發過渡到幀間狀態,隨機選擇與前一個不同的部分,本例中為第0部分。1:23:低音層和中音層繼續播放,現在播放來自第 0 部分的樣本。
1:23:旋律層再次開始播放。
1:42:旋律層完成第一次也是唯一一次重複播放。這次沒有暫停,立即觸發轉換到繼續狀態。
1:42:再次使用在狀態開始時選擇的部分。
1:42:旋律層使用與開始狀態相同的樣本洗牌。
2:09:旋律層完成第一次也是唯一一次重複播放。之後再次沒有暫停,立即觸發到完成狀態的轉換。2:09:第一部分被選擇為唯一尚未使用的部分。
2:35:旋律層完成第一次重複,繼續第二次重複,沒有停頓。
3:03:旋律層完成第二次也是最後一次重複。
第二個例子顯示了一個只有一個部分的軌道,但是當以固定的時間間隔在狀態之間轉換時,它會重疊。該部分有三層。如果在第一層播放特定樣本,也有機會播放間奏曲。
這聽起來可能是這樣的:
星期五報道#407 - 自動化配樂 (音頻#2)
技術挑戰
事實證明,當涉及到音樂時,正確的時機和過渡非常重要。我知道,我自己也很震驚。
樣本排按
樣本需要一個接一個地播放,沒有任何間隙,以保持軌道的整體節奏並避免音頻偽影,正如我們論壇上的某個人最近發現的那樣(https://forums.factorio.com/112836)。音樂播放器每秒更新60次,與遊戲邏輯的其餘部分相同。簡單地檢查當前樣本是否完成播放以開始播放下一個樣本是不夠的,因為它們之間可能有高達16.67ms(1s/60)的差距,破壞了節奏。將常規更新邏輯之外的檢查放入單獨的線程中,或者在樣本完成播放時使用回調也不起作用,因為我們正在使用的SDL_Mixer庫(版本2.0.4)(https://wiki.libsdl.org/SDL2_mixer/FrontPage) 將音頻數據混合在一起。
在我們當前的設置中,音頻以512個樣本的塊混合(這些是音頻信號樣本,而不是音樂樣本),採樣頻率為44.1kHz,混合間隔約為11.6ms。即使我們檢測到樣本完成播放的確切時刻,我們也無法在那一刻開始播放下一個樣本。會有一個長達11.6ms的間隙。我們真正需要的是一種方法來排隊我們的音樂樣本。
SDL_Mixer庫不提供這樣的功能,我需要自己在SDL_Mixer的基礎上構建它,並進行一些修改以SDL_Mixer。這不是我第一次需要在音頻後端添加功能,所以我沒有退縮,讓一個排隊系統工作得相當快。現在音樂播放器可以在其悠閒的16.67毫秒窗口中排隊樣本,一個單獨的進紙線程負責將樣本正確拼接在一起,而SDL_Mixer甚至不知道發生了這種情況。
樣本之間的轉換
正如您在上面的圖片中看到的,相同的樣本可以播放不同的長度。例如,在圖5的第一個示例中,首先為網格的三個單元播放3號樣本(黃色),然後為四個單元播放。除非我們想以多種長度保存相同樣本的變體(我們不想),否則我們通常需要在播放下一個樣本之前將樣本剪短。當你這樣做時,你可能會得到令人不快的音頻偽影或點擊。如果你想自己嘗試的話,當你在音樂或視頻播放器中改變播放位置時,也會發生類似的事情。發生的情況是,音頻信號的電平有一個很大的跳躍,聽起來像是點擊或彈出。如果我們想使用大詞,信號就會變得不連續。
你可以在這段使用舊版本拍攝的錄音中聽到剪輯偽影(你可能需要調高音量)。很明顯,必須對此採取措施。
星期五報道#407 - 自動化配樂 (音頻#3)
解決這個問題的方法是在短時間內淡出和/或淡入樣本,假設它們一個接一個地出現10毫秒。這樣過渡就很好,很流暢(連續)。您可能會發現一些音頻處理應用程序默認情況下會自動執行此類操作。聽起來很簡單,SDL_Mixer提供了一種淡入和淡出樣本的方法,有什麼問題嗎?SDL_Mixer的內置淡入功能計算在11.6毫秒間隔內混合的整個塊的相同目標音量。這意味著整個淡入將適合一個混合間隔,我們最終只有一個音量級別,因此根本沒有淡入。更不用說不可能準確地計時淡出。
幸運的是SDL_Mixer提供了對樣本附加過濾器(效果)的支持。在過濾器中,我們可以對音頻數據做任何我們想做的事情。編寫我們自己的具有樣本級精度的推子(同樣,這些是音頻信號樣本)作為過濾器是微不足道的。添加一個選項,將淡入淡出延遲到我們需要的確切時刻,瞧,不再有煩人的點擊。
對齊圖層
當涉及到所有層和子層一起演奏時,時間也很重要。它們需要對齊(同步)到由給定軌道的最小時間單位定義的時間網格中。第一個示例使用~286ms(12,600個樣本)時間單位。
當SDL的音頻線程混合時,它需要鎖定音頻設備以避免與其他線程(遊戲的其餘部分)競爭條件。在任何給定時間,只有一個線程應該更改活動的音頻資源。出於同樣的原因,當我們想開始播放樣本時,音頻設備被鎖定。
即使在軌道本身啟動後的一段時間內,可變軌道的某些層沒有開始播放,我們也不能等待啟動該層,因為我們無法在確切的時間啟動它,沒有“在x毫秒內開始播放”的功能,即使有,仍然會有我已經提到的混合塊的問題。所以我們需要同時啟動所有圖層以使它們對齊,不活動的圖層將播放靜音。
音頻線程可以隨時跳入並鎖定音頻設備。例如,如果我們在啟動總共五層中的前兩層時運氣不好,剩下的三層將在大約11.6毫秒後啟動,不對齊。SDL_Mixer不提供顯式鎖定功能。SDL本身確實提供了它們,但是SDL_Mixer試圖為大多數操作獲取鎖,所以無論如何都需要一些小調整,但添加起來並不難。
當一個圖層在軌道中間沒有播放任何東西時,也需要解決類似的問題。它不能被停止,因為我們無法在我們再次需要它的精確時刻重新啟動它。它只是靜默播放,與時間網格對齊。
上週常見問題
⚙ 音樂是否可以變得更有活力並且對遊戲環境做出反應嗎?
為了不涉及上週的技術細節,我們並不完全準確地說音樂播放器對遊戲狀態一無所知。顯然,如果它能對正在觀看的表面做出反應,它就會知道一些事情。然而,這是音樂播放器考慮的唯一遊戲狀態變量。
有這些限制的引擎並不意味著不可能將更多的信息傳遞到音樂播放器中,這是容易的部分。但是音樂本身必須從一開始就考慮到這樣的系統,幾年前。我們需要提前定義好所有這些條件,這樣彼得才能以一種協同工作的方式作曲。這些是困難的部分。我不是說這是不可能的,但我們走了一條不同的路。
長話短說,如果現在還沒有更有活力的音樂,它很可能不會發生。
⚙ 我們可以很好地控制音樂嗎?
引擎有這些限制並不意味著不可能向音樂播放器傳遞更多信息,這是容易的部分。但音樂本身的創作必須在多年前就考慮到這一系統。我們需要事先明確所有這些條件,這樣彼得就能以所有條件都能配合的方式進行創作。這些都是比較困難的部分。我並不是說這不可能做到,但我們走了一條不同的路。
⚙ 音樂會一直播放嗎?
長話短說,如果現在還沒有更有活力的音樂,那就很可能不會有了。
⚙ 太空時代原聲帶會單獨出售嗎?
是的,電子版本的配樂將可供購買,與基礎遊戲的配樂相同。