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

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

11-對part9-sound的翻譯和搬運(yùn)

2023-08-16 22:14 作者:Xdz-2333  | 我要投稿

非黑色字體均為我自己添加

原文在文章末尾

從音頻插口中播放聲音

????????我們游戲中丟失的一樣?xùn)|西就是聲音!一些嗶嗶和吱吱聲的加入會(huì)使得游戲更加吸引人.讓我們動(dòng)手吧!

????????我寫這些代碼的時(shí)候參考了?Peter Lemon的工作?(https://github.com/PeterLemon/RaspberryPi/tree/master/Sound/PWM/8BIT/44100Hz/Stereo/CPU),因此我很感激.它需要做一些很重要的改動(dòng)來在樹莓派硬件上運(yùn)行.

設(shè)計(jì)目標(biāo)

????????可能最終要的是,我們必須要在背景中播放音樂.如果我們的錄音播放綁定在CPU上,播放音頻時(shí)游戲就會(huì)暫停.我認(rèn)為任何玩家都立即會(huì)對這種粗魯?shù)那址阜锤?

????????一種解決方案就是實(shí)現(xiàn)多任務(wù),通過利用四個(gè)CPU核(目前我們只用了一個(gè)).這不是一項(xiàng)小舉動(dòng),對于幾聲嗶嗶聲和吱吱聲來講是很大的投入.

????????幸運(yùn)的是,樹莓派的硬件允許我們避免這種痛苦(對于現(xiàn)在),使用一個(gè)叫DMA的東西.這允許特定的硬件子系統(tǒng)來完全獨(dú)立于CPU訪問主存.

為自己記的筆記

????????這些事我這趟旅途上所學(xué)到的一些東西,它們幫了很重要的忙:

  • 當(dāng)GPIO40和41映射到選擇函數(shù)0時(shí),樹莓派使用PWM1作為音頻插口的輸出

  • 樹莓派時(shí)鐘震蕩頻率是54M赫茲

  • PWM1的DMA被映射到通道1

  • PWM1被映射到DREQ1

  • 我們必須使用遺留的主地址(從0x7E開始而不是0xFE)來為DMA轉(zhuǎn)移數(shù)據(jù)到外設(shè)上

  • DMA控制塊的結(jié)構(gòu)必須32位對齊

????????并且總是要開啟瀏覽器ARM外設(shè)文檔(https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf)的標(biāo)簽.它對于寄存器地址和位圖有非常詳細(xì)的參考.

音頻采樣格式

????????audio.bin 是我們將要播放的音頻文件.從技術(shù)上講,它是8-bit的44.1K赫茲的無符號PCM數(shù)據(jù).在現(xiàn)代它并不是一個(gè)常見格式,但是可以用ffmpeg(https://ffmpeg.org/)等工具進(jìn)行轉(zhuǎn)換.

????????為了將我們的 .bin 文件轉(zhuǎn)換為可以被電腦播放的 .wav 文件,做這個(gè):

ffmpeg -f u8 -ar 44.1k -ac 2 -i audio.bin audio.wav

????????將其轉(zhuǎn)換為我們的二進(jìn)制文件,做這個(gè):

ffmpeg -i audio.wav -f u8 -ar 44.1k -ac 2 audio.bin

????????這將幫助你對你自己的音頻采樣文件進(jìn)行編碼!這是我的歌聲的一段(啊!),伴隨著我妻子的木管樂器.另一旁,我十分推薦原始記錄(https://www.youtube.com/watch?v=k1UoUNC3Wj0)

使用CPU和PWM模塊測試錄音

????????我知道DMA傳輸可能比較棘手,所以我從證明我能從樹莓派的接口輸出音頻開始.

????????看到main(),你可以看到第一個(gè)函數(shù)調(diào)用 audio_init().這個(gè)函數(shù)確保PWM1被正確映射.PWM是用于使用數(shù)字信號控制模擬信號的技術(shù).由于數(shù)字信號只有開(1 - 全功率)和關(guān)(0 - 沒有功率),模擬信號可能是0到1之間無限位數(shù)字的一個(gè)值.PWM通過快速脈沖/脈沖調(diào)節(jié)電壓來偽造模擬信號.產(chǎn)生的平均電壓最終看起來像一個(gè)模擬信號,盡管它不是.明白了?

????????關(guān)于PWM的一點(diǎn)補(bǔ)充:

????????由于數(shù)字設(shè)備只有0和1,它是離散的,因此只能周期性的發(fā)出高低兩種電平,這在示波器上看起來就是方波,PWM可以調(diào)節(jié)的參數(shù)有三個(gè),頻率,占空比和電壓,它主要是通過不同的占空比來達(dá)到不同的平均輸出功率,從而驅(qū)動(dòng)電機(jī),音頻等設(shè)備.

????????這些脈沖/爆發(fā)確實(shí)需要很高的精確度來使其工作,所以我們需要可靠的時(shí)鐘源.就像我們的廚房的時(shí)鐘每秒滴答一下,樹莓派上的振蕩器有一個(gè)常規(guī)的"滴答" -- 在這個(gè)例子里 , 54,000,000 次每秒(54M赫茲).我們的音頻采樣在44.1k赫茲,所以我們需要使其"慢下來".我們做到這一點(diǎn),首先通過停止時(shí)鐘,然后設(shè)置一個(gè)分頻器,設(shè)置PWM的范圍,然后再次開啟時(shí)鐘.在我的代碼里,我使用2作為時(shí)鐘分頻(所以我們降到了27M赫茲),然后把范圍設(shè)置到612(0x264).實(shí)際上,這意味著我們的PWM模塊會(huì)在?27,000,000/612 次每秒移動(dòng)到下一個(gè)采樣上 -- 大約等于 44.1k赫茲,它恰好是被包括的音頻采樣文件 audio.bin 的采樣頻率(我已經(jīng)包括了 audio.wav 所以你也可以在你的電腦上正常的聽它!)

????????關(guān)于時(shí)鐘的一點(diǎn)補(bǔ)充:

????????電路里面的時(shí)鐘和平常說的時(shí)鐘概念不太相同,它本質(zhì)上是固定頻率的電流脈沖.不過萬事萬物只要是做周期性運(yùn)動(dòng)的都能拿來計(jì)時(shí),和尺子一樣,如果一個(gè)尺子最小刻度是1mm,那么它就測量不了1mm以下的物體的長度,同樣的,如果電路中的時(shí)鐘每秒有1千個(gè)脈沖,那么它就不能用來表示1ms以下的時(shí)間,因此時(shí)鐘頻率越高,對于時(shí)間的計(jì)量就越精確.另外,時(shí)鐘在數(shù)字電路中經(jīng)常被用來做同步信號,因此時(shí)鐘頻率越高,數(shù)字電路的狀態(tài)轉(zhuǎn)換速度越快.不過頻率過高也是有缺點(diǎn)的,因?yàn)殡娐返墓暮途w管電壓翻轉(zhuǎn)頻率的四次方成正比,所以過高的頻率會(huì)帶來嚴(yán)重的積熱.

????????那么它是怎么來的呢,一般有兩種方法,第一是振蕩電路產(chǎn)生,不過這種方法產(chǎn)生的時(shí)鐘頻率不高,質(zhì)量也達(dá)不到要求.另一種就是晶振,利用不同頻率的電流通過石英晶體的時(shí)候,特定的頻率會(huì)與晶體共振,而其他的頻率被衰減這一特點(diǎn)得到高質(zhì)量高頻率的脈沖電流,然后經(jīng)過PLL電路進(jìn)行倍頻或者分頻的到更高頻率的脈沖,再傳輸給各個(gè)部件使用.因此數(shù)字設(shè)備上會(huì)有一個(gè)"時(shí)鐘樹",這個(gè)搞過FPGA開發(fā)的同學(xué)就知道是怎回事了.

????????當(dāng)我們啟動(dòng)PWM模塊的時(shí)候,告訴他等待它的FIFO輸入的數(shù)據(jù),然后我們繼續(xù).直到我們開始填充緩沖區(qū),沒有音頻播放.

????????希望你可以注意到我們同樣設(shè)置了通道0和通道1.這是因?yàn)槲覀兪褂昧肆Ⅲw聲采樣.

開始音頻播放

????????在 playaudio_cpu()中我們使用CPU來驅(qū)動(dòng)我們的采樣數(shù)據(jù)到FIFO緩沖區(qū)中.這些采樣數(shù)據(jù)被編譯到內(nèi)核中,于我們在part7-bluetooth中對藍(lán)牙固件所作的那樣進(jìn)行引用(我們還是沒有完成SD卡的文件訪問.抱歉!).

????????這個(gè)代碼相當(dāng)?shù)淖晕艺f明化.在我們向左通道和右通道發(fā)送字節(jié)之間我們基本上只檢查了FIFO緩沖是否被填滿(記住,立體聲!).如果我們看到了錯(cuò)誤,我們清除它并繼續(xù).

????????這就是了...PWM會(huì)接收緩沖區(qū)中的數(shù)據(jù)并且發(fā)送它,PWM-風(fēng)格(冒充一個(gè)模擬信號),在一個(gè)正確的速度上(感謝我們的時(shí)鐘分頻器和PWM范圍調(diào)控器),發(fā)送給音頻接口.

使用DMA完成

????????在palyaudio_dma() 中我們第一個(gè)挑戰(zhàn)是DMA運(yùn)輸和FIFO寄存器是4個(gè)字節(jié)寬,所以我們的單字節(jié)采樣需要零填充.因?yàn)閿?shù)據(jù)是 unsigned char * 并且 safe 是(技術(shù)上的) unsigned int *,我們可以使用一個(gè)簡單的循環(huán)來完成它.?你會(huì)注意到safe是一個(gè)RAM內(nèi)的內(nèi)存地址,我們知道它超越了我們的代碼(io.h 里面定義了 SAFE_ADDRESS ).

????????我們設(shè)置DMA控制模塊.這是一個(gè)內(nèi)存結(jié)構(gòu),它將告訴DMA引擎執(zhí)行傳輸所需要知道的一切.它有非常多的文檔,但是值得指出的是DMA_DEST_DREQ 和 DMA_PERMAP_1.這些設(shè)置確保我們使用之前設(shè)置好的硬件時(shí)鐘來"同步"運(yùn)輸.如果我們不這么做,DMA引擎將要盡快完成它(我們的音頻聽起來不會(huì)很好!).SRC_INC 只是告訴DMA引擎在運(yùn)輸過程中遞增源地址.因?yàn)槲覀儗⒁3帜康牡刂窞槌A?因?yàn)槲覀兿M偸侵赶騊WM模塊的FIFO輸入.同樣注意PWM_LEGACY_BASE的使用而不是PWM_BASE來尋址外設(shè)內(nèi)存.這是樹莓派4硬件的另一個(gè)怪事!

????????注意我們最后設(shè)置 .nextconbk 為 0x00.這是告訴DMA引擎這個(gè)任務(wù)完成后沒有什么要做的了.如果我們需要無限循環(huán)這段音頻(沒有人那么需要我的歌聲!),我們可以再次簡單的設(shè)置地址到同一個(gè)塊控制結(jié)構(gòu)體.

????????隨著我們啟動(dòng)DMA引擎,音頻開始播放.然而,值得注意的是,我們立即回到mian(),CPU可以開始其他的事情,因此達(dá)到了我們的目的.

結(jié)論

????????我們現(xiàn)在準(zhǔn)備將聲音整合到我們的Breakout游戲中,但與其這樣做,不如讓我們進(jìn)入兔子洞,看看我們是否不能在單獨(dú)的核心上運(yùn)行CPU播放!我說過這很難,但我喜歡挑戰(zhàn).



原文如下

Playing sound from the audio jack

One thing our game is missing is the excitement of sound! Some beeps and squeaks would be a wonderful addition to make the gameplay more compelling. Let's work to do just that!


I wrote this code as I referenced [Peter Lemon's work](https://github.com/PeterLemon/RaspberryPi/tree/master/Sound/PWM/8BIT/44100Hz/Stereo/CPU), for which I am very grateful. It did need some significant modification to work on the Raspberry Pi 4 hardware.


Design goal

Perhaps most importantly, we must be able to play sounds in the background. If our audio playback ties up the CPU, then gameplay will stop whilst the sound is playing. I think any player would be immediately put off by the rude intrusion into their adventure!


One solution for this is to implement multi-tasking, thereby making use of the four CPU cores (so far we've only used one). This is no small feat, and a big commitment for a few beeps and squeaks.


Fortunately, the Raspberry Pi 4's hardware allows us to avoid this pain (for now), using something called DMA. This allows specific hardware subsystems to access main system memory completely independently of the CPU.


Notes to self

Here are a few things I learned on this journey, which helped me along significantly:


?* The Raspberry Pi 4 uses PWM1 for output on the audio jack when GPIO 40 and 41 are mapped to Alternate Function 0

?* The Raspberry Pi 4's clock oscillator frequency is 54 MHz

?* PWM1 DMA is mapped to DMA channel 1

?* PWM1 is mapped to DREQ 1

?* We must use Legacy Master (starting `0x7E`, not `0xFE`) addresses for DMA transfers to peripherals

?* The DMA Control Block structures must be 32-bit aligned


And always, always have a browser tab open on the [BCM2711 ARM Peripherals document](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf). It's a very handy reference for register addresses and bitmaps etc.


Audio sample format

_audio.bin_ is the audio file we'll be playing. Technically speaking, it's 8-bit, unsigned PCM data at 44.1 KHz. This is an unusual format in this modern day and age, but it's easily converted using a tool like [ffmpeg](https://ffmpeg.org/).


To convert from our _.bin_ file to a _.wav_ file that any laptop can play natively, do this:


`ffmpeg -f u8 -ar 44.1k -ac 2 -i audio.bin audio.wav`


To convert back to our binary format, do this:


`ffmpeg -i audio.wav -f u8 -ar 44.1k -ac 2 audio.bin`


This should help you try the code with your own audio samples! This one is a short excerpt of [me](https://isometim.es) singing (argh!), with my wife playing woodwind. As an aside, I highly recommend checking out [the original track](https://www.youtube.com/watch?v=k1UoUNC3Wj0).


Testing playback using the CPU and PWM module

I knew DMA transfers might be a tricky beast, so I began by just proving I could play audio to the jack output of the Raspberry Pi 4.


Looking at `main()`, you'll see that we first call `audio_init()`. This function ensures that PWM1 is correctly mapped. PWM is a technique used to control analogue devices using digital signals. Whilst digital signals are either on (1 - full power) or off (0 - no power), analogue signals may be an infinite number of values between 1 and 0. PWM fakes an analogue signal by applying power in quick pulses/bursts of regulated voltage. The resultant average voltage will end up looking roughly like an analogue signal, despite not being one. Clever, eh?


These pulses/bursts do need to be highly accurate for this trick to work, and so we need a reliable clock source. Just like your kitchen clock ticks every second, so the oscillator on the Raspberry Pi 4 has a regular 'tick' - in this case, 54,000,000 times per second (54 MHz)! Our audio sample is at 44.1 KHz though, so we need to 'slow it down'. We do this by first stopping the clock, then setting a clock divisor, setting the PWM range, and enabling the clock again. In my code, I use 2 as the clock divisor (so we're down to 27 MHz) and set the range to 612 (0x264). Essentially, this means that our PWM module will move to a new sample every 27,000,000/612 times per second - roughly equivalent to 44.1 KHz, which just happens to be the sample rate of the included audio sample _audio.bin_ (I've included _audio.wav_ so you can listen normally on your laptop too!).


We then enable the PWM module, telling it to wait for sample data on its FIFO input, and we're good to go. Until we start filling the buffer, no audio will play.


Hopefully you'll notice that we set both channel 0 and channel 1 up similarly. This is because we're working with a stereo sample.


Starting the playback

In `playaudio_cpu()` we use the CPU to drive our sample data into the FIFO buffer. The sample data is built into the kernel and referenced exactly as we did with the Bluetooth firmware file back in part7-bluetooth (we still haven't done SD card file access, sorry!).


The code is fairly self-documenting. We essentially check the FIFO buffer isn't full before we send the left channel byte and the right channel byte (stereo, remember!). If we see errors, we clear them as we go.


And that's it... The PWM will pick up the digital data in the buffer and send it, PWM-style (faking an analogue signal), at the right speed (thanks to our clock divisor/PWM range mastery), to the audio jack.


Doing it with DMA

In `playaudio_dma()` our first challenge is that DMA transfers and FIFO registers are 4 bytes wide, so our single byte samples need some zero-padding. Because `data` is an `unsigned char *` and `safe` is (technically) an `unsigned int *`, we can do this copy with a simple _for loop_. You'll notice that `safe` is a memory location in RAM which we know to be beyond our program code (`SAFE_ADDRESS` is defined in _io.h_).


We then set up the DMA Control Block. This is an in-memory structure that will tell the DMA engine everything it needs to know to perform the transfer. This is very well-documented already, but worth pointing out is `DMA_DEST_DREQ` and `DMA_PERMAP_1`. These settings ensure that we use our previously-set hardware clock to 'pace' the transfer. If we didn't do this, the DMA engine would just get it done as fast as it could (and our audio wouldn't sound great!). `SRC_INC` simply tells the DMA engine to increment the source address throughout the transfer. We'll be keeping the destination address constant though, since we want it to always point to the PWM module's FIFO input. Note also the use of `PWM_LEGACY_BASE` rather than `PWM_BASE` to address this peripheral memory. This is another quirk of the Raspberry Pi 4 hardware!


Note finally how we set `.nextconbk` to 0x00. This tells the DMA engine that there is no more work to do after this job is complete. If we wanted to loop the audio sample infinitely (nobody needs that much of me singing!), we could simply set this to address the same control block structure again.


As we enable the DMA engine, playback begins. Notably, however, we're returned to `main()` immediately and the CPU can get on with other things, thereby meeting our design goal.


Conclusion

We're now ready to integrate sound into our Breakout game. But rather than do that, let's go down a rabbit hole and see if we can't get CPU playback running on a separate core! I said it was hard, but I love a challenge.



11-對part9-sound的翻譯和搬運(yùn)的評論 (共 條)

分享到微博請遵守國家法律
织金县| 古田县| 安多县| 嘉义县| 大兴区| 会宁县| 光山县| 本溪市| 乐东| 宜宾县| 红原县| 大渡口区| 公安县| 顺昌县| 延寿县| 威宁| 桐梓县| 渝中区| 延津县| 磴口县| 谢通门县| 白银市| 抚顺县| 鲁甸县| 新蔡县| 高淳县| 盱眙县| 佛教| 嘉禾县| 石楼县| 北流市| 宝清县| 普兰县| 桐城市| 都江堰市| 洞头县| 广安市| 综艺| 遂昌县| 乐山市| 娱乐|