オーディオファイルをpowershellスクリプトにドラッグ&ドロップでmp3に変換する

Windows上でオーディオファイルをD&Dでmp3に変換するスクリプトを作成したのでメモ

何をしたいか

Windows上で、音質がそれほど重要ではないオーディオファイルを、サイズ圧縮を目的として、mp3に変換したいのです。

実際は他の方法でもっとサイズを効率的に落とせるのかもしれないですが、とりあえずmp3にしておけばいいだろうという考えです。

macOSだと変換してくれるワークフローのようなものが元々あるのですが、Windowsでは無いようなので自分で用意する必要があります。これをスクリプトで作成します。

巷には変換用のソフトウェアが溢れかえっているようなのですが、闇雲にソフトウェアをインストールするのは憚られるので、ffmpegを使えば自前のスクリプトでできるんじゃないかと思い立った次第です。

使うもの

使用するのは次の2つだけです。

  • powershell: スクリプト自体はショートカットを利用して使います。
  • ffmpeg: このソフトウェアに変換していただきます。

ファイルの指定は引数で渡すのは面倒だし、コンソールの立ち上げも手間なので、ドラッグ&ドロップ(D&D)で変換を開始するようにしました。

いまさらですが、ドラッグ&ドロップドラッグアンドドロップD&Dは同じことです。表記ゆれが少々あります。

準備

ffmpegの用意をします。ソースコードからビルドするか、ビルド済みのバイナリを用意するかの2通りの方法があります。

公式サイトのこちら: Download FFmpegから入手できます。ビルド済みのバイナリへのリンクもあります。自分はビルド済みバイナリを使っています。

powershellはWindowsに内蔵なので特に何もしません。D&D対応のためにスクリプトを作った後にそのスクリプトのショートカットを少々いじる必要はあります。これは後述します。

スクリプトの中身

次にスクリプトを作成します。拡張子が.ps1のファイルを好きなところに作り、次のような感じで記述します:

# シンプルにD&Dでmp3に変換する。GUIは結果のみ
# スクリプト単体では引数で指定する。

Add-Type -Assembly System.Windows.Forms


$file = ""
$successCount = 0
$errorCount = 0
$errorFiles = @()
foreach($file in $Args) {
    # ファイルのみ処理する
    if (Test-Path -Path $file -PathType Leaf) {
        # 親フォルダパスを取得
        # ファイル名末尾をmp3にして出力
        # 元がmp3の時は、_out.mp3にする。
        $parent = Split-Path -Path $file
        $filename = Split-Path -Path $file -Leaf
        $fileext = $filename.Split('.')[-1]
        $newfilename = ""
        if ($fileext -eq "mp3"){
            $newfilename = -join ($filename, "_out.mp3")
        }else{
            $newfilename = -join ($filename, ".mp3")
        }
        $newfilepath = Join-Path -Path $parent -ChildPath $newfilename

        # ここからffmpegに渡す
        # ffmpegのパスを正しく設定すること
        <ffmpeg.exeのパス> -i $file -vn -ac 2 -ar 44100 -b:a 128k -acodec libmp3lame -f mp3 $newfilepath
        # 成否をチェック
        if ($?) {
            # 成功なら、$successCount++
            $successCount++
        }else {
            # 失敗なら、$errorCount++、ファイル名を配列に追加。
            $errorCount++
            $errorFiles += $filename
        }
    }
}

# 終了時の表示
[System.Windows.Forms.MessageBox]::Show("成功:${successCount}件、失敗:${errorCount}件でした。`r`n元のファイルと同じ場所に作成されています。", "変換終了")

保存するときには文字コードに注意します。UTF-8 with BOMにしましょう。UTF-8では正しく動作しません。マイクロソフトはBOMが好き。

変換終了後にはウィンドウが表示されます。終わるまでは何も表示もされないので、きちんと動作しているかを見たいならタスクマネージャーを使いましょう(不親切)。

スクリプトだけでは、起動時に引数指定が必要でこれを毎回するのは面倒です。そこでD&Dできるようにしていきます。

ショートカットを作る

ショートカットを作成して、そこにファイルを投げ込めるようにします。

作成するショートカットはそのままではドラッグアンドドロップできないので、利用できるようにするためにプロパティを変更します。

やること:

  1. ショートカットを作成して、好きなところに配置する。
  2. ショートカットのプロパティを開く。
  3. リンク先を次のように変更する: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle hidden -ExecutionPolicy RemoteSigned -File <作成したps1ファイルのパス>
  4. 完了!

もしpowershell.exeの場所が違ったら読み替えてください。

ショートカットのプロパティのリンク先というのはこちらです:

.ps1ショートカットのプロパティの編集箇所を示す図

これでD&Dすれば変換できるようになります。

あとは、オプションなどのメモを下に書いておきます。

できないこと

このスクリプトではフォルダのドロップには対応していません。個人的にそういう操作をする予定がないためです。また、処理の中止もありません。どうしても中止したければタスクマネージャーから強制終了させましょう。

また、変換時にエラーが起きるとそのファイル名を記録していますが、その確認手段を用意していません。 ウィンドウに表示させればいいのですが、調査と調整が面倒でサボっています。

以降はコマンドやオプションのメモを残しておきます。間違いがあるかもしれませんのでご注意を。

powershellで使っていることのメモ

D&Dでは実質スクリプトに引数を指定するものなので、引数を取得する必要がある。引数を取得するには$Argsを使う。配列なので、foreachで一つずつ処理していく。

まずは、引数がファイルかどうかを確かめる。これはTest-Path -Path <file_path> -PathType Leafでチェックできる。ファイルは木構造でいう葉だからLeaf

出力するファイル名を決定するために、引数のファイルパスを、親ディレクトリのパスと、ファイル名に分ける必要がある。パスからファイル名の取り出しにはSplit-Path -Path <file_path> -Leafを使う。親ディレクトリのパスの取り出しはSplit-Path -Path <file_path>を使う。

取得したファイル名record.wavなどは、recordwavのように拡張子の前後で分かれていると、出力ファイル名を作りやすいので分割する。これは文字列中のドット.で分ける。$filename.Split('.')[-1]で拡張子が取得できる。これは.で分割した配列の一番後ろを参照できるので拡張子になる。上のファイル名の例だと["record", "wav"]に分かれる。

取得した拡張子がmp3ならば、元のファイル名に_out.mp3を付加して、mp3でなければ、元のファイル名に.mp3を付加する。以前の拡張子は残しておいている。 文字列の結合には、-join ($strA, $strB)を使用する。

親ディレクトリと出力ファイル名の結合には、Join-Path -Path <parent_dir_path> -ChildPath <filename>を使用する。セパレータを考えないで済む。

直前のコマンドの成否は$?で確認できる。

メッセージウィンドウでは、`r`nで改行できる。powershellのエスケープシーケンスは、バッククォート`(バックティックとも呼ばれる)であり、バックスラッシュではないことに注意する。

powershell自体のオプションのメモ

ps1スクリプト実行時のpowershell.exeに渡すオプション(パラメータ)のメモです。ショートカットの実行時のリンク先として使用します。

こちらを参考: PowerShell exe について - PowerShell | Microsoft Docs

-WindowStyle hiddenの指定は、コンソール画面の表示を抑制する。NormalMinimizedMaximizedHiddenから1つ指定できる。

-ExecutionPolicy RemoteSignedの指定は、セキュリティ確保のための実行ポリシーを指定する。RemoteSignedより厳しい設定だと、スクリプトの実行が行えなかったと思う。詳細はこちら: 実行ポリシーについて - PowerShell | Microsoft Docs

なお、-ExecutionPolicyはpoweshell全体でも定義できますが、個別に指定しています。意味があるかは分かりません…

-File script.ps1の指定は、実行するスクリプトを指定する。これより後ろの引数などはスクリプトに渡されることに注意。-Fileを一番最後に指定するべき。

ffmpegで使っているオプションのメモ

公式ドキュメント(ffmpeg Documentation)にありますが、使っているものを抜粋しておきます。

  • -iで入力ファイルの指定。input
  • -vnで映像を無しに。video none
  • -acで音声のチャンネル数の指定。audio channels
  • -arで音声のサンプリング周波数の指定。audio rate
  • -b:aまたは-abで音声のビットレートの指定。audio bitrate-abはドキュメントでは見当たりませんでしたがまだ使えるようです。
  • -acodecで音声のコーデックの指定。-codec:aのエイリアス。
  • -fでフォーマットの指定だが、自動で決定してくれるのでなくても問題なかった。format
  • 最後に出力先のパスを指定。

全然使ったことがないので、オプションの指定方法はよく分かってなかったりします。入力に対する設定と出力に対する設定とか複数の入力がどうとかあるようですが、その辺りは使ってないので未修です。

おわり

実現したいことはできたので満足です。書いてみると細かいところは分かってないなと実感します。

powershellは面倒ですね。VSCodeで書いていても補完は正しく効いてない気がするし、ネット上では古い?記法も溢れているし。自分の記述も古いのが混じっているかもしれません。特に最後のウィンドウの表示が不安です。GUIも部品の配置が面倒です。ビルダー的なものがあるといいんですけどね。

D&D以外の良い渡し方があるといいんですが。Dockerなどで扱えれば環境を気にせず出来るんだけどなとか思いました。知識が足りてないのもありそうです。精進せねば。

Windowsの細かいところも分かっていきたいな。

ひとまずしたいことはできたのでよし。

以上です。

コメント

タイトルとURLをコピーしました