本文提供將音頻提升音量的python代碼,一如既往地實用主義代碼。百無聊賴就一起學Python吧,反正閑著也是閑著!
ffmpy安裝:
pip install ffmpy -i https://pypi.douban.com/simple
不廢話上代碼。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2021/11/24 22:45
# @Author : 劍客阿良_ALiang
# @Site :
# @File : raise_audio_volume_tool.py
import os
import uuid
from ffmpy import FFmpeg
# 通過倍率提升
def raise_by_ratio(audio_path: str, output_dir: str, ratio):
ext=os.path.basename(audio_path).strip().split('.')[-1]
if ext not in ['wav', 'mp3']:
raise Exception('format error')
ff=FFmpeg(
inputs={
'{}'.format(audio_path): None}, outputs={
os.path.join(
output_dir, '{}.{}'.format(
uuid.uuid4(), ext)): '-filter:a "volume={}"'.format(ratio)})
print(ff.cmd)
ff.run()
return os.path.join(output_dir, '{}.{}'.format(uuid.uuid4(), ext))
# 通過分貝數提升
def raise_by_decibel(audio_path: str, output_dir: str, decibel):
ext=os.path.basename(audio_path).strip().split('.')[-1]
if ext not in ['wav', 'mp3']:
raise Exception('format error')
ff=FFmpeg(
inputs={
'{}'.format(audio_path): None}, outputs={
os.path.join(
output_dir, '{}.{}'.format(
uuid.uuid4(), ext)): '-filter:a "volume={}dB"'.format(decibel)})
print(ff.cmd)
ff.run()
return os.path.join(output_dir, '{}.{}'.format(uuid.uuid4(), ext))
代碼說明
1、raise_by_ratio和raise_by_decibel方法的參數都為音頻地址、輸出目錄地址、倍率(分貝數)。
2、倍率參數可以為浮點型,例如:0.5
3、分貝數可以為負值。
4、為了避免輸出文件文件名重復,使用uuid作為文件名。
5、音頻后綴格式校驗較少,如需添加自行添加。
驗證一下
準備音頻的音量,這里提供查看音頻的分貝數的ffmpeg命令。
ffmpeg -i C:\Users\yi\Desktop0952057553105332.wav -filter_complex volumedetect -c:v copy -f null C:\Users\yi\Desktop.txt
執行代碼
if __name__=='__main__':
print(
raise_by_ratio(
'C:\\Users\\yi\\Desktop\\660952057553105332.wav',
'C:\\Users\\yi\\Desktop\\', 2))
print(
raise_by_decibel(
'C:\\Users\\yi\\Desktop\\660952057553105332.wav',
'C:\\Users\\yi\\Desktop\\', -10))
倍率音量提升效果
增大了一點,不明顯,可能跟基礎的分貝數有關。
分貝數提升效果
這個數值比較精準。
沒什么好總結的。
雖無言,卻風雪而立。雖無言,卻花滿枝頭。雖無言,卻兀自凋零。——《蟲師》
如果本文對你有用的話,請給我一個贊吧,謝謝!
近工作需要,在C#里面要播放一個簡單的提示音,記錄一下主要步驟。
新建一個項目,把聲音文件加入到Resources.resx
播放一次就3行代碼:
System.IO.Stream str=Properties.Resources.beep;
System.Media.SoundPlayer snd=new System.Media.SoundPlayer(str);
snd.Play();
搞定收工!
天我和一些朋友一起調試代碼,他們做程序員這一行都不太久,我向他們展示了一些代碼調試技巧。
今天早上我在想,我應該如何教授他們學習代碼調試?我在Twitter上發了一條推文說,我從來沒有見過任何好的調試代碼的指南。像往常一樣,我得到了很多有幫助的回答,現在我對如何教授代碼調試技巧/描述調試過程有了些想法。
調試資源
我希望有更多的關于代碼調試的書籍/指南,在這里我有兩個推薦:
David Agans 寫的《Debugging》:有幾個人向我推薦了這本《Debugging》,它看起來是一本很好的關于代碼調試的書,用簡短的篇幅闡述了一些代碼調試策略。這本書我還沒有讀過,但是我已經買了一本,我希望我讀完后決定是否應該推薦它。這本書中闡述的一些代碼調試應該遵循的規則似乎很有道理,比如說“了解系統”,“讓它失敗”,“別想了,先看看”,“分而治之”,“一次只改變一件事情”,“保持審查詳細記錄”,“從一個新的角度看問題”,和“如果你沒有修復它,它就不會修復”等等。另外,這本書還有一張吸引人的的代碼調試的海報。
John Regehr寫的“How to debug(如何調試)”:How to Debug是John Regehr基于他自己在大學里教授嵌入式系統課程的經驗寫的一篇非常好的博客文章(
https://blog.regehr.org/archives/199),里面有很多針對代碼調試的好建議。他還發表了一篇博文(
https://blog.regehr.org/archives/849)來評論4本關于代碼調試的書籍,包括了David Agans s寫的這本《Debugging》。
重現你的bug(但是要怎么做?)
接下來在這篇文章里,我將嘗試整理大家針對我的關于代碼調試的推文發來的各種不同的觀點和看法。
從這些看法中很明顯地看出,所有人都同意這一點:如果你想弄清楚發生了什么,那么能夠持續地重現一個bug非常重要。我對如何做到這一點有直覺,但是對于怎樣才能從“我看到這個bug兩次”跨越到“我可以根據需要在筆記本電腦上持續地再現這個bug”這一點,我不知道怎么解釋,而且我想知道你用來調試的技術是否依賴于這些不同的開發領域:后端web開發,前端開發,移動開發,游戲開發,C++編程,嵌入式開發等等。
快速重現bug
所有人也都同意,能夠快速地重現bug是非常有用的(如果每次更改都需要3分鐘來檢查是否有幫助,那么迭代就太慢了)。
這里有一些建議的方法:
對于那些需要在瀏覽器中進行很多次點擊才能重現的bug,用Selenium記錄你點擊的內容,并讓Selenium重播UI交互(詳細的建議請見這里:https://twitter.com/AnnieTheObscure/status/1142843984642899968);
如果你能夠的話,編寫一個重現錯誤的單元測試。這樣做還有另外一個好處:如果這個單元測試有意義的話,你可以稍后將它添加到測試套件中;
編寫一個腳本,或者找到一個命令行命令幫助你做它(比如curl MY_APP.local/whatever))。
承認bug可能是你寫的代碼引起
有時我看到一個問題,我會說“哦,X庫有個bug”,或者“哦,這是DNS錯誤造成的”,或者“哦,不是我的代碼,而是其它地方的錯誤造成的”。確實有時候一個bug不是我寫的代碼造成的!但一般來說,在一個已經驗證的庫和我上個月編寫的代碼之間,通常是我上個月編寫的代碼才是真正的問題所在 。
開始實驗
@act_gardnerd在Twitter上給出了一個很好的簡短的回答(
https://twitter.com/act_gardner/status/1142838587437830144),解釋了你在再現你的bug之后,你需要做什么。原文如下:
我試著鼓勵人們首先對這個bug有個全面的理解,比如說:什么正在發生?你期望會發生什么?什么時候會發生?什么時候不發生?然后運用他們對系統的心理模型來猜測可能發生的破壞,并進行實驗。實驗可以是更改或刪除代碼,從一個REPL調用API,嘗試新的輸入,使用調試器(debugger)或print語句來獲取內存中的值。
我認為這里可能需要循環地重復以下步驟:
猜測可能發生的錯誤的某一個方面(比如說,“這個變量被設置為X,它應該是Y”,或“發送到服務器的請求是錯誤的”,或“這段代碼根本沒有運行過”等等)。
做實驗來驗證這個猜測。
重復循環,直到你明白發生了根源所在。
一次只改變一件事情——所有人都肯定地同意,在做實驗來驗證一個假設時,一次只改變一件事情是很重要的。
檢查你的假設
很多調試工作都基于一個假設:你確定的事情是真的(比如說:“等一下,這個請求是要發送到新服務器,對吧,不是舊服務器????)。但是實際上……不是真的。我試圖列出一些常見的錯誤假設。下面是一些例子:
此變量設置為X(“該文件名絕對正確”);
該變量的值不可能在X和Y之間變化;
這段代碼以前沒有問題;
此函數執行X;
我正在編輯正確的文件;
我寫的那一行代碼不可能有任何拼寫錯誤,只是一行代碼而已;
文檔是正確的;
我正在查看的代碼在某個時刻被執行;
這兩段代碼是按順序執行的,而不是并行執行的;
這段代碼在調試模式和發布模式下編譯(使用或不使用-O2開關,或…)時,會做同樣的事情;
編譯器沒有錯誤(這是故意放在最后的一個錯誤,很少有人會認為編譯器會出錯)。
獲取信息的奇招
有很多正常的方法可以做實驗來檢查你對代碼所做的假設/猜測(比如,打印變量值,使用調試器,等等)。但是,有時候你所處的環境更為困難,你無法打印出內容,也無法訪問調試器(可能是執行這些操作不方便,因為要處理的事件太多)。這里有一些應對方法:
在手機上添加聲音:“在移動開發世界里,這條建議給了我很大幫助。Xcode可以在你遇到斷點時播放聲音(并且代碼不停止而繼續執行下去)。我把它們放在代碼中的某個位置,然后聽嗡嗡的叮當聲來指示代碼中發生的錯誤”(欲知詳情,請查看上面提到的推文)。
關于使用Xcode播放iOS代碼調試的聲音,這里(https://qnoid.com/2013/06/08/Sound-Debugging.html)有一些很有趣的討論。
添加發光二極管(LED):“很久以前,當我們在Transputer網格上做嵌入式開發時,我們將發光二極管連接到每個芯片的一個未使用的管腳上。它在診斷并行性問題上出奇地有效。”
string: “我的網絡教授告訴我這樣一個故事,在早期的以太網時代,他在施樂公司(Xerox)看到了一個黑客:他使用一個帶有放大器,馬達和一根繩子的同軸電纜接頭。網絡越忙,線就轉得越快。”
Peep是一個“Network Auralizer”,可以將系統上發生的事情轉換成聲音。我花了10分鐘試圖讓它編譯,但迄今為止失敗了,但它看起來很有趣,我想繼續嘗試它!!
這里我想重點強調一下:信息是最重要的,你需要做任何必要的事情來獲取信息。
編寫代碼使其更易于調試
一些人提到的另外一個觀點是:我們可以改進程序,使其更加易于調試。tef對此有一篇很好的文章:編寫易于刪除和調試的代碼(
https://programmingisterrible.com/post/173883533613/code-to-debug)。我覺得下面這一點很正確:
可調試的代碼并不一定干凈,而充斥著檢查或錯誤處理的代碼很少能讓人愉快地閱讀。
我個人認為:“易于調試”的一種解釋是“每當出現錯誤時,程序都會以易于理解的方式向你準確地報告發生的事情”。每當我的程序有問題并且報告這樣的錯誤信息“Error:無法連接到某個IP的端口443:連接超時”時,我都想說:“謝謝,這就是我想知道的事情”。有了這樣的錯誤信息,我就可以檢查我是否需要修復防火墻,或者我是否由于某種原因得到了錯誤的IP地址。
最近我碰到一個簡單的例子:我向一個我寫的服務器發出請求,得到的回應是“upstream connect error or disconnect/reset before headers”。這是一個nginx錯誤,在本例中基本上是因為“程序在響應一個請求而發送任何內容之前崩潰了”。找出崩潰的原因是很容易的,但是有更好的錯誤處理方式(返回錯誤而不是崩潰)可以節省我一點時間,因為我不必去檢查崩潰的原因,我只需閱讀錯誤信息,知道發生了什么就可以了。
錯誤消息好過無提示的程序失敗
為了更接近“每次出現錯誤時,程序都會以一種易于理解的方式向你報告發生的事情”的夢想,你還需要遵守這條“立即返回錯誤消息”的鐵律,而不是默默地向另一個功能寫入不正確的數據或者傳遞無意義的數據,誰都不知道它會拿這些數據做什么,結果只會讓你頭痛。要做到這點,意味著你要添加如下代碼:
if UNEXPECTED_THING:
raise "oh no THING happened"
獲得正確的錯誤信息并不容易,因為你在程序當中哪里犯了錯誤并不總是顯而易見的,但是這樣做確實有很大幫助。
failure:返回一堆錯誤,而不僅僅是一個錯誤
為了返回更加易于調試的有用錯誤,Rust提供了一個非常令人難以置信的錯誤處理庫failure,它基本于允許你返回一系列錯誤,而不僅僅是一個錯誤,因此你可以打印出一堆錯誤,如:
"error starting server process" caused by
"error initializing logging backend" caused by
"connection failure: timeout connecting to 1.2.3.4 port 1234".
這比僅僅返回connection failure: timeout connecting to 1.2.3.4 port 1234本身要有用得多,因為它還告訴你和IP 1.2.3.4有關的其它一些重要的信息(比如上面這個錯誤就顯示它和日志后端有關!)。我認為它也比返回帶有堆棧跟蹤信息的connection failure: timeout connecting to 1.2.3.4 port 1234的錯誤信息更加有用:因為它將堆棧跟蹤信息中的關鍵的出錯部分總結出來,這樣你就不需要讀取堆棧跟蹤中的每一行(因為其中一些可能不相關!).
其它語言中的類似于Rust語言failure庫的工具有:
Go語言:它的習慣用法似乎是把你的一堆錯誤串成一個大字符串,這樣你就得到了一長串的像這樣的錯誤提示:“error:第一個錯誤:error:第二個錯誤:error:第二個錯誤”。它工作得很好,但是它的錯誤信息的結構比failure庫能提供的要差得多。
Java語言:我聽說Java可以給出異常的原因(Causes of exceptions), 但是我自己沒有用過。
Python 3:你可以使用raise ... from設置異常的“__cause__”屬性,然后你的異常將被這句話分開:The above exception was the direct cause of the following exception:..
如果你知道其它語言中如何處理程序錯誤的方法,請告訴我,我會很感興趣!
了解錯誤消息的含義
我經常理所當然地認為代碼調試的一個子技巧是:正確理解錯誤消息的含義!我在這里(
https://pythonforbiologists.com/29-common-beginner-errors-on-one-page/)看到了這個很好的圖形,它解釋了常見的Python錯誤以及它們的含義,并且將一些錯誤如 NameError, IOError,等等分離開來。
我認為解釋錯誤消息很困難的一個原因是理解一個新的錯誤消息可能意味著學習一個新的概念。比如,NameError可能代表“你的代碼使用了一個它定義的變量作用域之外的一個變量”,但是要真正理解它的意思,你首先得搞清楚什么是變量作用域。我在學習Rust的時候經常碰到這樣的問題,Rust編譯器會提示我“你有一個奇怪的lifetime錯誤”,而我就會想“呃,好吧,Rust,我知道了,現在我就去搞清楚lifetime是如何工作的!”
很多時候,錯誤消息都往往是由一個與消息文本根本不相干的錯誤引起的,比如說“upstream connect error or disconnect/reset before headers”這個錯誤可能意味著“Julia,你的服務器崩潰了!”當你切換到一個新的開發領域時,理解錯誤消息的技能通常是不可轉移的(假如我明天開始大量地編寫React或其它編程語言的代碼,一開始我可能根本不知道任何錯誤消息的含義!)。所以這個問題絕對不僅僅是初學者需要面臨的問題。
結語
當我在談到代碼調試技巧時,我總感覺我遺漏了一件重要的事情,那就是對人們在代碼調試中哪里會遇到困難的一種更深入的理解。通常我們很容易說:“好吧,你需要重現這個問題。那么先讓我們進行最小化的重現,你可以開始猜測和驗證你的猜測,改進你對系統的思維模式,找出問題所在,然后解決問題。最后寫一個測試,希望它不再重現”,但是,實際上,我們很難確定人們到底會在哪里遇到困難和最難的部分是什么。對我自己而言代碼調試最難的地方是什么,我通常會有點思路。但是對那些新人而言,代碼調試最難的地方是什么,依然是云里霧里,毫無頭緒。
聲明:本文來源于網絡,如有侵權,請聯系我刪除。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。