質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.80%

mfccを求めるプログラムのエラーの解消

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,663

gopan

score 19

前提・実現したいこと

とあるサイトを参考にして類似楽曲検索システムの作成がしたいのですが、その第一歩にメル周波数ケプストラム係数(mfcc)を求めるプログラムを作成しました。実行してみたところ、以下のようなエラーが起きてしまいました。
なお、このエラーは自分が自宅で使用しているPCで起きたもので、大学の研究室のPCでは問題なく動作したのでプログラムが間違っているということは恐らくないと思います。一応下のほうにプログラムも載せておきます。
以上のことから自分のPCの開発環境などに問題があるとおもうのですが、そういったPCの環境構築に関して初心者と同じようなものなので、以下のエラーの解決方法が分かりません。
なので今回質問させていただきました。
どうかよろしくお願いします。

発生している問題・エラーメッセージ

λ python mp3_to_mfcc.py mp3 mfcc raw
lame: excess arg Greed.mp3'
Could not find "temp.mp3".
Can't init infile 'temp.mp3'
sox FAIL formats: can't open input file `temp.wav': No such file or directory
lame: excess arg temp.mp3
Could not find "temp.mp3".
Can't init infile 'temp.mp3'
sox FAIL formats: can't open input file `temp.wav': No such file or directory
Could not find "'mp3\02-霈昴″縺ェ縺後i窶ヲ.mp3'".
Could not find "temp.mp3".
Can't init infile 'temp.mp3'
sox FAIL formats: can't open input file `temp.wav': No such file or directory
lame: excess arg temp.mp3
Could not find "temp.mp3".
Can't init infile 'temp.mp3'
sox FAIL formats: can't open input file `temp.wav': No such file or directory
lame: excess arg temp.mp3
Could not find "temp.mp3".
Can't init infile 'temp.mp3'
sox FAIL formats: can't open input file `temp.wav': No such file or directory

該当のソースコード

#coding:utf-8
import os
import sys

# mp3_to_mfcc.py
# usage: python mp3_to_mfcc.py [mp3dir] [mfccdir] [rawdir]
# ディレクトリ内のMP3ファイルからMFCCを抽出する

def mp3ToRaw(mp3File, rawFile):
    # mp3を16kHz, 32bitでリサンプリング
    os.system("lame --resample 16 -b 32 -a '%s' temp.mp3" % mp3File)
    # mp3をwavに変換
    os.system("lame --decode temp.mp3 temp.wav")
    # wavをrawに変換
    os.system("sox temp.wav %s" % rawFile)
    os.remove("temp.mp3")
    os.remove("temp.wav")

def calcNumSample(rawFile):
    # 1サンプルはshort型(2byte)なのでファイルサイズを2で割る
    filesize = os.path.getsize("temp.raw")
    numsample = filesize / 2
    return numsample

def extractCenter(inFile, outFile, period):
    # 波形のサンプル数を求める
    numsample = calcNumSample(inFile)

    fs = 16000
    center = numsample / 2
    start = center - fs * period
    end = center + fs * period

    # period*2秒未満の場合は範囲を狭める
    if start < 0: start = 0
    if end > numsample - 1: end = numsample - 1

    # SPTKのbcutコマンドで切り出す
    os.system("bcut +s -s %d -e %d < '%s' > '%s'" \
              % (start, end, "temp.raw", rawFile))

def calcMFCC(rawFile, mfccFile):
    # サンプリング周波数: 16kHz
    # フレーム長: 400サンプル
    # シフト幅  : 160サンプル
    # チャンネル数: 40
    # MFCC: 19次元 + エネルギー
    os.system("x2x +sf < '%s' | frame -l 400 -p 160 | mfcc -l 400 -f 16 -n 40 -m 19 -E > '%s'"
              % (rawFile, mfccFile))

if __name__ == "__main__":
    if len(sys.argv) != 4:
        print "usage: python mp3_to_mfcc.py [mp3dir] [mfccdir] [rawdir]"
        sys.exit()

    mp3Dir = sys.argv[1]
    mfccDir = sys.argv[2]
    rawDir = sys.argv[3]

    if not os.path.exists(mfccDir):
        os.mkdir(mfccDir)
    if not os.path.exists(rawDir):
        os.mkdir(rawDir)

    for file in os.listdir(mp3Dir):
        if not file.endswith(".mp3"): continue
        mp3File = os.path.join(mp3Dir, file)
        mfccFile = os.path.join(mfccDir, file.replace(".mp3", ".mfc"))
        rawFile = os.path.join(rawDir, file.replace(".mp3", ".raw"))

        try:
            # MP3を変換
            mp3ToRaw(mp3File, "temp.raw")

            # 中央の30秒だけ抽出してrawFileへ
            extractCenter("temp.raw", rawFile, 15)

            # MFCCを計算
            calcMFCC(rawFile, mfccFile)

            print "%s => %s" % (mp3File, mfccFile)

            # 後片付け
            os.remove("temp.raw")
        except:
            continue

試したこと

SPTK、lame、soxは無事にインストールされていると思うので、環境変数に間違いがあると思い見直したのですが、間違いはないと思います。

補足情報

○自宅PC
OS : Windows10
コンソール : cmder
エディター : VScode
python2系

○研究室PC
OS : Linux
コンソール : Terminal
エディター : emacs
python2系

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

コマンドラインの引数のエスケープのしかたはオペレーティングシステムによって違います。今回の場合で言うと、引数を'...'で囲うのはUnix系のシェルの多くで動作しますが、Windowsのcmd.exeでは期待通りにならないようです。いずれにせよ、ファイル名に'が入っていればうまく動きません。

さらに言うと、誰かがファイル名を工夫したファイルを作って処理するファイルにまぎれこませておくことで、質問者さんがプログラムを実行したときにlame以外の任意のコマンドを実行させることもできてしまいます。恐ろしいことです。

subprocess.call()などではコマンドライン文字列の代わりに引数のシーケンスを渡せるので、シェルの引数処理にかかわる上のような問題が起きません。

import subprocess
...

subprocess.call(['/path/to/lame', '--resample', '16', '-b', '32', '-a', mp3File, 'temp.mp3'])

(11/04 10:19のコメント後に追記)

リダイレクトについては次のように書けると思います。

fdin = open('temp.raw', 'rb')
fdout = open(rawFile, 'wb')
subprocess.call(['bcut', '+s', '-s', str(start), '-e', str(end)], stdin=fdin, stdout=fdout)

open()の第2引数の「'rb'」「'wb'」は、Linuxの場合は「'r'」「'w'」でもかまわないのですが、Windowsではbが必要です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/04 10:19

    連続してすみません。
    ちなみに今は3系の最新バージョンを使用しています。

    キャンセル

  • 2016/11/10 08:56

    本当に何度もすみません

    os.system("x2x +sf < '%s' | frame -l 400 -p 160 | mfcc -l 400 -f 16 -n 40 -m 19 -E > '%s'"
         % (rawFile, mfccFile))

    この部分はどのようにしたら良いのでしょうか?


    キャンセル

  • 2016/11/10 13:06

    うむ。
    複数のsubprocess.call()に分けて処理することもできますが、大変ですね。
    ファイル名以外の部分は変数ではなく決まった文字列なので、回答に書いたような危険はないですね。だから、以下のようにしてもいいのでは。
    fdin = open(rawFile, 'rb')
    fdout = open(mfccFile, 'wb')
    subprocess.call('x2x +sf | frame -l 400 -p 160 | mfcc -l 400 -f 16 -n 40 -m 19 -E', stdin=fdin, stdout=fdout, shell=True)

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る