從零開始學習PYTHON3講義(十四)寫一個mp3播放器

<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML'></script>html

《從零開始PYTHON3》第十四講

一般來講,Python解釋執行,運行速度慢,並不適合完整的開發遊戲。隨着電腦速度的快速提升,這種狀況有所好轉,但開發遊戲仍然不是Python的重點工做。
大多應用是利用Python開發效率高的特色,進行遊戲原型驗證,或者在大的遊戲系統中,使用Python進行地圖、場景等定製。還有就是使用遊戲開發的技術和理念,將Python用於商業視覺展現、工程效果展現。python

原型驗證:指的是有了一個好的遊戲想法,完整的開發出來確定須要大量的人員、費用、時間,利用Python編程簡單高效的特色,先模擬完成一部分遊戲的功能,從而可以展現給投資人、客戶,獲取你們的承認,進而獲得經費投入。
地圖、場景定製:遊戲的開發確定須要不少專業技術方面的高精尖人才,但遊戲的運營、地圖的設計、故事情節等。這都是商業或者藝術方面的專業強項,而這些人員不大可能使用c/c++等經常使用的遊戲開發工具來作這些工做。所以,遊戲開發過程當中,一般完成Python語言的接口,讓這些商業、藝術工做人員也能使用比較方便的手段進行遊戲功能的調整。c++

此外,現代的遊戲開發已是一個大團隊合做的產物,已經很是難以單打獨鬥完成一款遊戲。因此學習遊戲編程的目標並非但願本身獨立完成一個遊戲,而是用這種思路來解決具體問題。
一般遊戲開發的工做分工是這樣的:
gameRD 其中音效、畫面都會由更專業的團隊完成。最後由程序人員集成在遊戲中。在遊戲中,音樂音效、操做控制、遊戲邏輯、畫面幾個部分,都是並行在同時進行的。它們必須共同生效,遊戲纔會好玩。ajax


Pygame編程和音樂播放

Pygame是一個強大的遊戲擴展包,首先也是安裝:編程

#使用管理員模式啓動cmd命令行,而後執行:
pip install pygame  #某些系統是pip3 install pygame

這個安裝擴展包的過程,咱們重複了不少遍,這個算是最後一遍了。由於Pygame是咱們課程講解的最後一個擴展包。比起來其它的軟件,Python的擴展包,只要你知道了名字,安裝幾乎都是相同的。即使不一樣的操做系統,差異也不大。flask

在這一講,咱們會採用跟之前不一樣的方法來說述Pygame擴展包的使用。緣由是Python有很是多的擴展包。即使官方內置的擴展包,也量很是大。若是徹底等待別人教你使用這種方式是不可能的,此外即使是別人教過了,Python和擴展包的升級也很是的快。原有的使用方法,極可能如今已經不適用了。這些都要求你有本身探索的能力,在Python基本技能的學習掌握以後,根據本身的編程需求,選擇相應的擴展包,查找資料、文檔。在網上資料的幫助下,掌握擴展包的使用方法。網絡

從目前行業內的使用狀況看,最大的障礙在於目前主要的文檔來源都是英文的,這要求咱們具有必定的英文閱讀能力。此外,雖然版本的更新對擴展包的使用有一些差異,但這種差異畢竟不算大。因此在國內一些相對較早的文檔幫助下,再對應國外新版本的文檔,也能下降你的學習門檻。架構


只是播放mp3,Python有不少擴展包能夠選,不少操做起來也更簡便。不過pygame是爲了遊戲設計,除了背景音樂,音效、與畫面的協做也考慮的更多。因此雖然用起來複雜一些,咱們依然仍是選擇學習用Pygame播放mp3音樂。目的,更可能是指望學習者除了學習python相關的知識,也更多理解現代計算機併發多任務和多種約束條件下的編程思惟。併發

拿到一個新的擴展包,一般你有這樣幾種途徑瞭解它的使用:框架

  • 到官網查看官方文檔(一般是英文)

  • 在搜索引擎網站好比百度搜索中文的資料,這種狀況比較多見,由於大多狀況下,你之因此知道這個擴展包,也是在網上搜索相關資料的時候,別人介紹的。而一般這種狀況下,都已經有包簡單實用的介紹。

  • 使用Python內置的dir()/help()函數,當前仍是英文資料,適合已經瞭解擴展包的基本架構,只是在函數選擇、調用的時候查找資料

    因此,實際上,經過搜索引擎查找相關資料,應當是你上手的最優選擇。以pygame爲例,經過查找中文的資料,總結以後,應當能寫出這樣的程序:

#MP3播放器

#引入擴展庫
import pygame
    
#歌曲文件
file='rongHua.mp3'

#初始化聲音庫
pygame.mixer.init(frequency=44100)
print("播放音樂-絨花")

#載入音樂文件
pygame.mixer.music.load(file)
#播放聲音
pygame.mixer.music.play()

程序每一條語句都有註釋,大概的框架上看,應當也是順序執行的。有一些參數可能你還不能明白,好比frequency=44100,不過應當不影響你抄過來用。這個是指定音頻庫使用的採樣頻率,44100通常已是高保真音樂的採樣頻率了。一般mp3文件都是這種格式。另外忘了交代,rongHua.mp3是咱們要播放的聲音文件名稱,記得要提早準備好,放到程序同一個目錄。

執行程序以後發現,詭異的事情發生了,程序只顯示了一行文字:「播放音樂-絨花」,而後就退出了,並無事情發生,也沒有音樂播放出來。

一開始就說過了,本講重點不徹底是播放一首音樂,而是但願能引導你們使用探索的方式,來了解一個新的擴展包如何學習和使用。因此不要等待着我說出答案,而是積極的思考,判斷出現了什麼問題,而且嘗試去解決。

首先要說明的是,程序自己引入pygame庫、庫的初始化還有播放語句語句自己都並無什麼錯誤。一般在網上查找資料的時候,只要認真閱讀,比較容易保證這一點。難以立刻學會並應用到編程中的,是關於某個庫「架構」方面的內容,也就是影響程序結構方面的內容。若是以爲這句話比較抽象的話,你能夠回憶一下上一講咱們嘗試過的flask網絡編程框架。框架、架構,這兩個詞在這裏基本能夠劃等號了。

咱們的程序沒有能播放出來音樂,也是這方面的緣由。
一般遊戲程序要包含至少4部分的內容,咱們用本講開始的那張圖來講明,音樂、畫面、操控、邏輯這四部份內容是並行運行,相互配合,才能展示給用戶一個圖文並茂、流暢、吸引人的遊戲。
所以做爲遊戲的一部分,音樂的播放也不可能像咱們前面學過的繪圖、計算等操做同樣,在音樂沒有播放完成前,程序中止在那裏一直等待。事實上一般遊戲的作法都是,發出播放音樂的命令以後,命令自己立刻返回,讓程序有能力並行去處理按鍵輸入、繪圖等動做。
而在咱們上面的程序中,播放這個命令確定是發出去了,但沒有等音樂聲響起,程序就已經結束退出了。程序的結束退出將自動的釋放程序打開的各項資源,清理運行的痕跡,從而音樂也就不可能再放出來了。
這僅僅是咱們推測分析的結果,咱們來證實一下,方法就是在程序最後增長一行語句:

#程序等待5秒鐘
pygame.time.delay(1000*5)

使用這樣語句的目的是,若是咱們上面的推測成立,那確定要對程序作結構上的調整。這個工做量會比較大,因此咱們先使用簡單的語句來驗證一下咱們的思考。
再次運行程序,你會聽到音樂響了5秒鐘,而後程序退出,音樂也中止了。
這基本能夠證實,咱們的思考正確。此外彷佛還有些別的問題,好比音樂一開始有一個「破音」,這讓人感受很差。並且程序彷佛有的時候能正常播放,有的時候仍是不穩定,沒法播放成功。
下面要如何改進程序呢?

一般咱們會繼續在網上搜索pygame模塊使用的案例,閱讀別人的程序,有的時候運氣好,你碰到的程序代碼,跟你想寫的代碼是徹底相同的功能,這時候你能夠拷貝過來直接使用。但大多時候,你只能找到功能相近的代碼,因此仍然須要你閱讀別人的程序,並從其中學習對你有用的部分。
好比,你可能搜索到咱們第一講演示的遊戲,其中固然也有聲音處理的部分,你會重點閱讀這部分的代碼,來找出同本身程序的區別,以求解決問題。

在這個過程當中,咱們又作出了一些判斷,固然這些判斷依然須要大量程序的經驗,因此並不能要求初學者也能輕易作到。但複雜的作不到,你能夠從簡單的入手,逐漸積累。這裏只是想告訴你正確的學習思路:

  • Pygame做爲一個遊戲開發庫,聲音的播放須要依賴一個窗口,也就是遊戲的畫面。沒有窗口的狀況下,播放進程沒法穩定的工做。這一項緣由推測來自於,不少網上找到的代碼,在聲音處理上並無太多不一樣,但能正常工做,因此會有這樣的猜想。
  • Python的各個功能,初始化通常意味着創建各項必須的資源,完成工做後,退出以前,應當釋放掉這些資源,特別是系統公用的聲音、顯示等,若是程序只是退出,沒有釋放,就可能致使再次運行的時候,聲音沒法正確完成初始化,畢竟一個系統的設備,是被全部程序所公用的。
  • 系統自己緣由,不能快速的連續的初始化及釋放,兩次運行之間應當等待片刻。這個判斷,在屢次運行程序,查找規律的過程當中,能很快的發現,固然須要你足夠的細心觀察。
  • 「破音」是由於在聲音設備初始化後,還沒有穩定以前就開始發送音頻數據,此時的數據沒法被正常解析,形成破音。這僅爲猜想,須要實驗的證明。

驗證思考最好的辦法就是修改程序,而後再次運行實驗,所以咱們再完成一版程序:

#引入擴展庫
import pygame
    
#歌曲文件
file='rongHua.mp3'

#初始化pygame顯示庫
pygame.display.init()
#打開一個窗口
screen = pygame.display.set_mode([200,100])
#初始化pygame聲音庫
pygame.mixer.init(frequency=44100)
print("播放音樂-絨花")
#載入音樂文件
pygame.mixer.music.load(file)
#保存當前音量
v = pygame.mixer.music.get_volume()
#設置爲靜音,防止開始的爆破音
pygame.mixer.music.set_volume(0)
#播放聲音
pygame.mixer.music.play()
#延時0.2秒打開聲音,避過爆破音
pygame.time.delay(200)
pygame.mixer.music.set_volume(v)
#播放5秒鐘
pygame.time.delay(1000*5)
#中止播放
pygame.mixer.music.stop()
#退出聲音庫和顯示庫
pygame.mixer.quit()
pygame.display.quit()

每一行代碼都有註釋,我只講解跟上一版不一樣的代碼:

  • 初始化的時候打開一個窗口,雖然什麼也沒有顯示,但讓播放器有了載體。
  • 一開始關閉聲音,延時再打開音量,避開一開始的爆破音。
  • 程序退出前關閉播放,釋放各項資源。

此外這些工做中,用到了不少新的函數,這些函數一開始你並不可能知道。這些函數的學習通常是兩個方向,一是概要的瀏覽pygame的手冊或者幫助,在心中有一個粗的概念,這樣用到什麼功能的時候,你會想起來可能有某個函數能完成這個功能,而後再精細查看。第二是但願用到某個功能,在網上查找使用Python或者pygame如何作到這個功能。固然還有另一種渠道,有可能你直接搜索到了功能相近的代碼,從中間直接抄過來使用。

試運行以後咱們開心的發現,穩定性問題和爆破音都解決了,剩下最關鍵的,如何完整的播放音樂文件?
這涉及到了咱們前面講過的程序結構問題,也是一個框架型的程序庫對程序結構的要求。這一部分通常沒有好辦法,只能經過閱讀官方的文檔或者閱讀其它程序的成熟代碼來獲取,這個過程通常會較長。好在咱們大多狀況下不會上來就碰到這麼複雜的問題,都是按部就班。而且大多的擴展包只是增長功能性的函數,並不要求程序的結構有多少改變。

咱們經過一張對比圖來講明pygame對程序結構的要求:
python3-14.001 傳統程序雖然咱們不怎麼熟悉聲音處理,但結構咱們都比較熟悉。程序中可能有循環,但整體是串行執行的,完成一件事情,纔去作另一件。
從外觀上看,右側的遊戲程序結構,跟左側不過多了一個循環。但你要記得,這裏面每一項都是並行執行的,每個步驟並不會等待這一項工做作完,就會返回接受新的命令,因此程序的聲音、圖像、程序邏輯、鍵盤控制,纔可能一塊兒發生做用。
這種並行處理的程序,同傳統的程序比,有不少不可協調的理念區別,pygame爲了作到並行,採用了「事件驅動」的理念來完成這種控制。
事件驅動實際是存在好久的編程方式了,通常傳統的Windows程序,都使用微軟公司提供的消息循環,來處理全部的窗口事件。Python pygame的事件處理,也是採用相似的機制。
總結一下使用事件驅動的方式來編寫pygame程序的要點:

  • 聲音、圖像、鍵盤鼠標輸入、遊戲邏輯必須並行進行,任何一個局部不能長時間無限制的執行(網絡編程實際也是並行的,但在小型網站項目中,沒有體現那麼清晰和嚴格)
  • 各個環節之間的同步、配合,都是經過互相發送消息的方式來完成的。從獨立一個功能(模塊)角度來看,每每是獲得某個消息以後,開始進行某項任務,這種方式叫作事件驅動
  • 各類消息都是經過核心的消息傳遞模塊完成的,程序的主循環通常就是不停的讀取消息,根據消息的定義分發給不一樣模塊,並執行不一樣功能,也稱爲消息循環

咱們根據剛纔這些理念,從新改寫程序,這個程序最終造成code4.py,這裏只介紹重點的消息循環部分:

#... 初始化及基本播放代碼忽略...
#自定義一條消息(一個事件)用於表示播放結束
#pygame.USEREVENT是pygame中預約義的用戶消息起始值
MUSIC_END = pygame.USEREVENT + 1
#設置當前音樂播放完成後,發送自定義的消息
pygame.mixer.music.set_endevent(MUSIC_END)

#延時0.2秒打開聲音,避過爆破音
pygame.time.delay(200)
pygame.mixer.music.set_volume(v)

#定義一個退出程序標誌
requireQuit = False
#程序主循環
while not requireQuit:
    #循環接受各類事件
    for event in pygame.event.get():
        #若是是自定義的播放完成消息
        if event.type == MUSIC_END:
            requireQuit=True  #退出
            break
        #界面窗口菜單關閉申請
        elif event.type == pygame.QUIT:
            requireQuit=True
            break
        #有鍵盤擡起
        elif event.type == pygame.KEYUP:
            #q鍵
            if event.key == pygame.K_q:
                requireQuit=True
                break
#... 退出操做 ...

程序中,咱們本身定義了一條消息。所謂消息,並非日常人類喜聞樂見的一條短信或者語音,其實就是一個整數數字。爲了容易記憶,咱們固然本身定義了一個變量名來表明它,但實際它就是一個數字。
緣由是對計算機來說,其實一切都是數字,咱們用一個字符串反而讓計算機執行的更慢。
隨後,由於咱們的消息循環中確定還可能嵌套循環,一個break語句只能打破內部的循環,並不能讓外部循環也退出,因此咱們定義了一個bool的變量,來表示程序是否須要退出循環。
這裏的消息循環從技術上並無啥難度,主要是你須要適應這麼多新的函數和預約義的變量(這裏固然當作常量來用,好比表示pygame須要退出)。
在內部循環中,咱們判斷了三種可能須要退出的消息。一是本身定義的,若是音樂播放結束,應當退出;二是用戶用鼠標關閉窗口,程序應當退出;三是按q鍵表示用戶但願退出播放。
按下按鍵遊戲採起相應動做是很常見的遊戲處理工做,咱們在這裏等待用戶按下按鍵而後再鬆開的這一刻退出,這樣防止用戶按下q鍵一直沒有鬆手所致使的程序退出後,屏幕上還會出現不少q字符的狀況。

如今的程序已經能正常的播放音樂了,實際上咱們的程序還能進一步優化。好比1.添加播放的時間顯示;2.向前向後跳轉播放。
這兩個功能均可以在消息循環中處理,這樣程序纔是並行的。如今你可能感受到了,實際上消息循環中,纔是程序的主要邏輯。的確如此,其實全部的遊戲基本都是在消息循環中作全部的主要工做,固然具體工做細節,都是由已經定義好的函數或叫子程序來具體執行完成的,在主循環中,只是對這些函數的組織、管理和調用。

顯示播放位置:

#程序主循環
while not requireQuit:
    #獲取當前播放位置
    pos=pygame.mixer.music.get_pos()
    #顯示
    print("Playing:", pos,end='\r')

消息循環中,在按鍵部分添加代碼:

#若是是向右鍵,則前跳10秒
   elif event.key == pygame.K_LEFT:
      pygame.mixer.music.set_pos(pos/1000-10)
  #若是是向左鍵,則後跳10秒
   elif event.key == pygame.K_RIGHT:
      pygame.mixer.music.set_pos(pos/1000+10)

這樣的功能增長,依賴於你對pygame擴展庫愈來愈熟悉,經過閱讀文檔,發現pygame擴展庫能提供什麼樣的功能。而這個功能你又須要,就能夠加入到程序中。


練習時間

其實本講能夠說從開始到如今都是挑戰,所以沒有再設置單獨的挑戰環節。

咱們直接進入練習的環節:

  • 以本講前面最終版代碼code5.py爲藍本,修改程序,實現由命令行參數接受mp3文件名,並播放
  • 除了q鍵以外,請設定ESC鍵也做爲退出按鍵。提示,ESC鍵的代碼爲:pygame.K_ESCAPE

本講小結

  • python並非很適合進行遊戲編程,但遊戲編程的學習能讓你的程序更友好,並具備豐富的表現力
  • 並行、事件驅動的編程思想,是現代程序開發的前沿思想,對於提升程序的效率和穩定性有重要的幫助
  • 在一個新模塊的學習中,按部就班,逐步完善代碼是經常使用的一種手段。在本講,咱們更側重講述,你接觸到一個新的擴展包,如何查找資料、分析問題,最終掌握它的使用

練習答案

請參考mp3Player.py程序。
(全部本系列中出現、使用過的源碼將會在連載完成後統一整理提供下載。)

原文出處:https://www.cnblogs.com/andrewwang/p/10208319.html

相關文章
相關標籤/搜索