【轉載翻譯】音頻從零開始:產生第一個聲音(Golang)

翻譯自:https://dylanmeeus.github.io/posts/audio-from-scratch-pt1/

在這篇文章中,我們將使用Go從頭開始以二進制格式創(chuàng)建聲音。這篇文章的最終結果是播放一定頻率、采樣率和持續(xù)時間的聲音。我們還會應用指數(shù)衰減,這樣聲音就會逐漸變小。

在最簡單的形式中,聲音對計算機可以被認為是一種簡單的數(shù)字編碼波。在聲音到達你的耳朵之前,它會經過一個數(shù)字到模擬轉換器,基本上就是把數(shù)字信號轉換成你的耳機/揚聲器的電流。

第一步,我們試著用go創(chuàng)建一個正弦波。我們可以用math.sin (x)來生成它并將x作為弧度傳遞。我們必須在一定范圍內迭代才能得到正弦波。為了保持在音頻節(jié)目領域,“點”的數(shù)量,我們將繪制到正弦波是我們的樣本。(如果你想跳過,這篇文章的所有代碼在github上:https://github.com/DylanMeeus/MediumCode/blob/master/Audio)

const nsamps = 50 // samples to generate?

func generate()?

{?

?tau = math.Pi * 2?

?var angle float64 = tau / nsamps?

?for i := 0; i < nsamps; i++ {?

?samp = math.Sin(angle * float64(i))?

?fmt.Printf("%.8f\n", samp)?

?}?

}

注意,我們將示例打印到stdout,我們可以將此輸出通過管道傳輸?shù)揭粋€文件(go run main.go > out.txt)?。這個文件的輸出如下所示:

-0.00000000?

-0.12533323?

-0.24868989?

-0.36812455?

-0.48175367?

-0.58778525

. .很難看出這里發(fā)生了什么。但是使用gnuplot,我們可以更容易地可視化這個文件。在gnuplot,運行:

plot "out.txt" with lines

這看起來像一個完美連續(xù)的正弦波,但這就是gnuplot“用線”來顯示它的方式。如果我們畫條形圖,我們會看到稍微不同的結果。( plot “out.txt” with boxes )

既然我們可以產生正弦波,我們就有了發(fā)聲的基本知識。盡管這只是浮點數(shù),我們可以把它變成一些可播放的原始音頻文件。

第二步:產生聲音

要把正弦波變成真正的聲音,我們需要引入一些東西。

樣本率

首先,以一定的采樣率來存儲聲音。采樣率告訴你每秒有多少采樣用于你的聲音編碼。cd質量的記錄有44100赫茲的采樣率,允許頻率高達22.05KHz。考慮到人耳聽到聲音20 hz 20 khz之間,這是很多(假設你只是針對人類聽眾)。雖然其他格式是可能的,如48Khz的dvd視頻質量或96KHz的dvd音頻質量,我們將堅持目前的cd質量。正如您將看到的那樣——更改這一點是很簡單的。你們可以自己嘗試一下看看是否能聽到不同的聲音。所以我們不使用nsamps = 50我們至少需要44100個樣本。為了調整聲音的持續(xù)時間,我們還將為此添加一個變量。

const (?

? ? Duration = 2?

? ? SampleRate = 44100?

)

頻率

接下來,我們將引入一個頻率。目前,我們將使用頻率的440Hz被定義為“音高標準”。這是一個高于中間c的音符A的標準調音,為了不偏離我們產生音樂的目標,如果你好奇我們?yōu)槭裁词褂眠@個頻率,請查看這個維基頁面。加上這個,我們將再次擴展我們的觀點:

const (?

?Duration = 2?

?SampleRate = 44100?

?Frequency = 440 // Pitch Standard?

)

存儲聲音

我們現(xiàn)在有了生成聲音的基本要素,但是我們漏掉了一個至關重要的部分。我們如何存儲這些數(shù)據(jù),以便我們的計算機能將其解釋為聲音?我們在第1步中生成的浮點數(shù)確實可以使用,但是我們必須將它們存儲為二進制表示。這里一個棘手的部分是,你必須以你的計算機能夠讀取的方式存儲它們——這意味著你必須在BigEndian機器上使用BigEndian,否則就只能使用LittleEndian。在linux系統(tǒng)上,這可以通過您的終端發(fā)現(xiàn)(macOS上可能有相同的命令,但不需要驗證!)

dylan@devuan:~$ lscpu | grep "Byte Order" Byte Order: Little Endian


代碼!

現(xiàn)在我們知道該做什么了,并且設置好了常數(shù),讓我們修改生成函數(shù)來把它們聯(lián)系在一起。聲音將被存儲在一個名為“out”的文件中。在你的機器上。(為簡潔起見,我已經刪除了錯誤處理!)

func generate() {?

?nsamps := Duration * SampleRate

var angle float64 = tau / float64(nsamps)?

?file := "out.bin"?

?f, _ := os.Create(file)?

?for i := 0; i < nsamps; i++ {?

?sample := math.Sin(angle * Frequency * float64(i))?

?var buf [8]byte?

?binary.LittleEndian.PutUint32(buf[:], math.Float32bits(float32(sample)))?

?bw,_ := f.Write(buf[:])?

?fmt.Printf("\rWrote: %v bytes to %s", bw, file)?

?}?

}

使用ffplay,我們現(xiàn)在可以播放這個文件,盡管我們需要指定我們的采樣率和格式。指定我們的顯示模式,我們也可以可視化的聲音正在播放:

ffplay -f f32le -ar 44100 -showmode 1 out.bin

或者,您也可以使用Audacity將我們的二進制文件作為“原始音頻文件”導入。只要確保你選擇單聲道和正確的編碼。這是如何創(chuàng)建的音高標準。雖然一個小小的改進是在接近結尾的時候篡改聲音。這比有一個恒定的信號感覺更“自然”。為了實現(xiàn)這一點,我們可以在信號的末端引入指數(shù)衰減。擴展1:指數(shù)衰減我們不需要添加很多就能得到指數(shù)衰減。我們想讓我們的信號淡出,所以我們將定義一個開始和結束“振幅”來產生衰減因子。接下來,在每次迭代中,我們將通過將信號乘以一個衰減因子來修改信號的實際振幅。在函數(shù)的頂部,我們將定義這些變量:

func generate() {?

?var (?

?start float64 = 1.0

?end float64 = 1.0e-4?

?)?

?nsamps = Duration * SampleRate

decayfac := math.Pow(end/start, 1.0/float64(nsamps)) ..

一旦我們設置好了它們,在我們生成wave的循環(huán)中,我們可以在每次迭代中修改樣本

sample := math.Sin(angle * Frequency * float64(i))?

sample *= start?

start *= decayfac

當我們把這些放在一起,我們的函數(shù)變成:

func generate() {?

?var (?

?start float64 = 1.0

end float64 = 1.0e-4?

?)?

nsamps := Duration * SampleRate?

?var angle float64 = tau / float64(nsamps)?

?file := "out.bin"?

?f, _ := os.Create(file)

decayfac := math.Pow(end/start, 1.0/float64(nsamps))?

?for i := 0; i < nsamps; i++ {?

?sample := math.Sin(angle * Frequency * float64(i))?

?sample *= start?

?start *= decayfac?

?var buf [8]byte?

?binary.LittleEndian.PutUint32(buf[:], math.Float32bits(float32(sample)))?

?bw, _ := f.Write(buf[:])?

?fmt.Printf("\rWrote: %v bytes to %s", bw, file)?

?}?

}

現(xiàn)在如果我們播放這個聲音,我們會從這篇文章頂部的視頻中得到聲音。所有代碼都在GitHub上:


https://github.com/DylanMeeus/MediumCode/blob/master/Audio/FirstSound/main.go

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容