Pygame出現播放背景音樂卡頓的問題分析及解決(發(fā)生在win10更新至win11后)
Pygame是常用的游戲開發(fā)庫之一。然而在使用Pygame的過程中,卻出現了播放背景音樂卡頓的問題。表現為咯咯咯的噪音。
檢查Pygame版本,為2.5.2。降級至1.9.6,此時代碼報錯:
Traceback (most recent call last): File "D:\MyWork\Code_Learning\PythonLearning\Pygame\test2.py", line 4, in <module> pygame.mixer.init() pygame.error: No available audio device
沒有可用的音頻設備。
改到2.0.0版本,代碼又能正常運行,但卡頓再次出現。Python版本3.8.10。
看來這個問題大概是Pygame與底層音頻驅動的交互問題??赡芩也坏秸r寗?,調用了兼容驅動,最終被套了多層接口,使得整體播放效率下降,音頻出現卡頓。但我無法直接解決,這是Pygame自身的問題。
解決辦法:
1. 改用winsound庫來播放音樂。
壞處是必須使用wav格式的音頻,占用較大。
好處是終于聽見了正常的音樂聲。
python內置庫,所以不用安裝。
2. 改用playsound庫來播放音樂。
然而,這個第三方庫有一些小bug。需要將原文件的第55行:
command = ' '.join(command).encode('utf-16')
更改為:
command = ' '.join(command)#.encode('utf-16')
不需要主動尋找文件。報錯時會自動說文件的路徑:
Error 305 for command: open "C:\Users\16581\AppData\Local\Temp\PS_hj5h9ji.mp3" 在用引號括起的字符串不能指定額外的字符。 Error 263 for command: close "C:\Users\16581\AppData\Local\Temp\PS_hj5h9ji.mp3" 指定的設備未打開,或不被 MCI 所識別。 Failed to close the file: "C:\Users\16581\AppData\Local\Temp\PS_hj5h9ji.mp3" Traceback (most recent call last): File "D:\MyWork\Code_Learning\PythonLearning\Pygame\test2.py", line 8, in <module> playsound.playsound("./src/Hello_How are you.mp3") File "D:\MyWork\Code_Learning\PythonLearning\Pygame\Runtime3.8\lib\site-packages\playsound.py", line 44, in _playsoundWin _playsoundWin(tempPath, block) File "D:\MyWork\Code_Learning\PythonLearning\Pygame\Runtime3.8\lib\site-packages\playsound.py", line 72, in _playsoundWin winCommand(u'open {}'.format(sound)) File "D:\MyWork\Code_Learning\PythonLearning\Pygame\Runtime3.8\lib\site-packages\playsound.py", line 64, in winCommand raise PlaysoundException(exceptionMessage) playsound.PlaysoundException: Error 305 for command: open "C:\Users\16581\AppData\Local\Temp\PS_hj5h9ji.mp3" 在用引號括起的字符串不能指定額外的字符。
根據路徑打開這個playsound.py文件(Pycharm就直接點路徑點開)即可。
之所以報這個錯,是因為python3默認的是utf-8的編碼方式,而不是utf-16。這個第三方庫有些畫蛇添足??傊サ艏纯?。
可以播放mp3文件。相較于第一個辦法,減小了游戲發(fā)布大小。
此外playsound這個庫還可能在安裝時報錯。當我從Python3.8.10遷移至3.11.8時,pip安裝出現:
Collecting playsound Using cached playsound-1.3.0.tar.gz (7.7 kB) Installing build dependencies ... done Getting requirements to build wheel ... error error: subprocess-exited-with-error × Getting requirements to build wheel did not run successfully. │ exit code: 1 ╰─> [29 lines of output] Traceback (most recent call last): File "/data/data/com.termux/files/usr/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 353, in main() File "/data/data/com.termux/files/usr/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 335, in main json_out['return_val'] = hook(**hook_input['kwargs']) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/lib/python3.11/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 118, in get_requires_for_build_wheel return hook(config_settings) ^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/tmp/pip-build-env-pa_2_lv3/overlay/lib/python3.11/site-packages/setuptools/build_meta.py", line 338, in get_requires_for_build_wheel return self._get_build_requires(config_settings, requirements=['wheel']) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/tmp/pip-build-env-pa_2_lv3/overlay/lib/python3.11/site-packages/setuptools/build_meta.py", line 320, in _get_build_requires self.run_setup() File "/data/data/com.termux/files/usr/tmp/pip-build-env-pa_2_lv3/overlay/lib/python3.11/site-packages/setuptools/build_meta.py", line 485, in run_setup self).run_setup(setup_script=setup_script) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/tmp/pip-build-env-pa_2_lv3/overlay/lib/python3.11/site-packages/setuptools/build_meta.py", line 335, in run_setup exec(code, locals()) File "", line 6, in File "/data/data/com.termux/files/usr/lib/python3.11/inspect.py", line 1262, in getsource lines, lnum = getsourcelines(object) ^^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/lib/python3.11/inspect.py", line 1244, in getsourcelines lines, lnum = findsource(object) ^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/lib/python3.11/inspect.py", line 1081, in findsource raise OSError('could not get source code') OSError: could not get source code [end of output] note: This error originates from a subprocess, and is likely not a problem with pip. error: subprocess-exited-with-error × Getting requirements to build wheel did not run successfully. │ exit code: 1 ╰─> See above for output. note: This error originates from a subprocess, and is likely not a problem with pip.`
最后解決辦法就是更新一下wheel就行了:
pip install wheel --upgrade
源于這個discussion: How do I install "playsound" in Python? · termux/termux-app · Discussion #3306 (github.com)
需要注意的是,使用多進程配合playsound播放音樂時,需要將pygame的初始化至少放在if __name__ == "__main__"這一層下,否則會創(chuàng)建兩個窗口。
需要注意的是,使用多進程配合playsound播放音樂時,需要將pygame的初始化至少放在if __name__ == "__main__"這一層下,否則會創(chuàng)建兩個窗口。
3. 使用pydub.AudioSegment和pydub.playback播放音頻
這個方案支持的音頻格式是最廣的。因為可以通過安裝pyaudio的方式來增加支持。
但這個方案也有問題。播放16位音頻的話,一切正常。但當音頻文件為32位的時候,播放音頻就啞聲了。
我寫了一個復雜的sounddevice輸出流程序,測試下來讀取的pcm數據是沒有問題的,可以正常播放。但我不想把這個程序帶到我的主要工程中,一個是太復雜,第二是我還沒有實現多個音頻播放時怎么定位要終止的一個音頻。
那么播放部分pydub.playback究竟是出了什么問題呢?我正在尋找。一個可能的原因是chunk._data(播放時播放的采樣片段)是32位整數,但輸出到pyaudio的stream.write()當中時,它需要的又是32位浮點。p.get_format_from_width(song.sample_width)可以看到得到的是所謂的"paFloat32",但我在sounddevice的實現程序中明明是把數據當做整數來處理的。
重寫了playback的_play_with_pyaudio()方法。
def _play_with_pyaudio(seg): import pyaudio p = pyaudio.PyAudio() stream = p.open(format=p.get_format_from_width(seg.sample_width), channels=seg.channels, rate=seg.frame_rate, output=True) # Just in case there were any exceptions/interrupts, we release the resource # So as not to raise OSError: Device Unavailable should play() be used again try: # break audio into half-second chunks (to allows keyboard interrupts) for chunk in make_chunks(seg, 500): data = chunk._data need = bytearray(len(data)) for i in range(0, len(data) // 4, 4): f = (int.from_bytes(data[i:i + 4], byteorder='little', signed=True) / float(1 << (seg.sample_width * 8 - 1))) need[i:i + 4] = struct.pack('<f', f) need = bytes(need) stream.write(need) finally: stream.stop_stream() stream.close() p.terminate()
通過除以一個float(1 << (song.sample_width * 8 - 1)),這個值是32位有符號整數的最大值,然后就有了一個[-1, 1)的浮點數,能夠送入音頻流中播放。
但是實際效果并不好。播放時非??D??磥碚麛缔D浮點的過程必須放在外面進行。畢竟用C語言的時候都是直接用指針讀值的,哪里需要這些復雜的封裝工程。
還有一個問題是我這個方法還只適用于32位音頻,其他音頻沒做適配。還需要一些編程。
最后是用numpy成功解決了卡頓的問題。并加上了采樣寬度(采樣深度)的判斷。
def _play_with_pyaudio(seg): import pyaudio import numpy as np p = pyaudio.PyAudio() stream = p.open(format=p.get_format_from_width(seg.sample_width), channels=seg.channels, rate=seg.frame_rate, output=True) # Just in case there were any exceptions/interrupts, we release the resource # So as not to raise OSError: Device Unavailable should play() be used again try: # break audio into half-second chunks (to allows keyboard interrupts) if seg.sample_width == 4: for chunk in utils.make_chunks(seg, 500): samples = chunk.get_array_of_samples() samples = np.array(samples, dtype=np.float32) samples /= 1 << (seg.sample_width * 8 - 1) stream.write(samples.tobytes()) # stream.write(chunk._data) # print(chunk._data[:500]) # print(song.get_array_of_samples()[:500]) # os.system('pause') else: for chunk in utils.make_chunks(seg, 500): stream.write(chunk._data) finally: stream.stop_stream() stream.close() p.terminate()
這是一個有些惱人但不嚴重的bug。人們遇到這個問題,估計會把所有音效文件改成16位來播放。但我非要用原格式不可。
此外,也許應該用array.array來轉換更好一些,與原代碼傾向的用法保持一致。但我不知道怎么才能除出來一個float32的數組,array.array我又不熟。所以姑且就這么實現了。后面有時間再來熟悉熟悉這個所謂的array.array。貌似它是python自帶的,不用自己安裝。
比較疑惑的是,32位音頻怎么才能判斷它是浮點數的32位,還是整數的32位。AudioSegment找了一圈都沒有看到有關的API,但是get_array_of_samples貌似輸出的一定是整數數組。總之,一切都還需要在array.array中揭曉。
最后,將p.get_format_from_width(song.sample_width)替換為pyaudio._portaudio.paInt32也能正常播放。
以上就是Pygame出現播放背景音樂卡頓的問題分析及解決(發(fā)生在win10更新至win11后)的詳細內容,更多關于Pygame播放背景音樂卡頓的資料請關注腳本之家其它相關文章!
相關文章
python3 使用Opencv打開USB攝像頭,配置1080P分辨率的操作
今天小編就為大家分享一篇python3 使用Opencv打開USB攝像頭,配置1080P分辨率的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12