web-dev-qa-db-ja.com

wavオーディオファイルのダウンサンプリング

外部のPythonライブラリを使用せずに、44100Hzから16000Hzにwavファイルをダウンサンプリングする必要があるため、waveaudioopを使用することをお勧めします。 wavファイルはsetframerate関数を使用して16000にフレームレートしますが、それだけで録音全体が遅くなります。オーディオファイルを16kHzにダウンサンプリングし、オーディオの長さを同じに保つにはどうすればよいですか?

13
d3cr1pt0r

Librosaのload()関数を使用できます。

import librosa    
y, s = librosa.load('test.wav', sr=8000) # Downsample 44.1kHz to 8kHz

Librosaをインストールするための余分な労力は、おそらく安心する価値があります。

Pro-tip:LibrosaをAnacondaにインストールするときは、 install ffmpeg も必要なので、

pip install librosa
conda install -c conda-forge ffmpeg

これにより、NoBackendError()エラーが保存されます。

17
wafflecat

ご回答ありがとうございます。私はすでに解決策を見つけました、そしてそれはとてもうまくいきます。これが全体の関数です。

def downsampleWav(src, dst, inrate=44100, outrate=16000, inchannels=2, outchannels=1):
    if not os.path.exists(src):
        print 'Source not found!'
        return False

    if not os.path.exists(os.path.dirname(dst)):
        os.makedirs(os.path.dirname(dst))

    try:
        s_read = wave.open(src, 'r')
        s_write = wave.open(dst, 'w')
    except:
        print 'Failed to open files!'
        return False

    n_frames = s_read.getnframes()
    data = s_read.readframes(n_frames)

    try:
        converted = audioop.ratecv(data, 2, inchannels, inrate, outrate, None)
        if outchannels == 1:
            converted = audioop.tomono(converted[0], 2, 1, 0)
    except:
        print 'Failed to downsample wav'
        return False

    try:
        s_write.setparams((outchannels, 2, outrate, 0, 'NONE', 'Uncompressed'))
        s_write.writeframes(converted)
    except:
        print 'Failed to write wav'
        return False

    try:
        s_read.close()
        s_write.close()
    except:
        print 'Failed to close wav files'
        return False

    return True
6
d3cr1pt0r

scipyでresampleを使用できます。 bytestringネイティブからpythonへの型変換とscipyで必要な配列との間で行われる型変換があるため、少し頭痛の種です。別の頭痛の種です。Pythonのwaveモジュールでは、データが署名されているかどうかを判別する方法がないためです(8ビットまたは16ビットの場合のみ)。両方で機能するはずですが、テストしていません。 。

以下は、8ビットと16ビットのモノラルを44.1から16に変換(符号なし)する小さなプログラムです。コードの先頭で入力/出力名を編集します。コマンドライン引数を使用することはありませんでした。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  downsample.py
#  
#  Copyright 2015 John Coppens <[email protected]>
#  
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#  
#

inwave = "sine_44k.wav"
outwave = "sine_16k.wav"

import wave
import numpy as np
import scipy.signal as sps

class DownSample():
    def __init__(self):
        self.in_rate = 44100.0
        self.out_rate = 16000.0

    def open_file(self, fname):
        try:
            self.in_wav = wave.open(fname)
        except:
            print("Cannot open wav file (%s)" % fname)
            return False

        if self.in_wav.getframerate() != self.in_rate:
            print("Frame rate is not %d (it's %d)" % \
                  (self.in_rate, self.in_wav.getframerate()))
            return False

        self.in_nframes = self.in_wav.getnframes()
        print("Frames: %d" % self.in_wav.getnframes())

        if self.in_wav.getsampwidth() == 1:
            self.nptype = np.uint8
        Elif self.in_wav.getsampwidth() == 2:
            self.nptype = np.uint16

        return True

    def resample(self, fname):
        self.out_wav = wave.open(fname, "w")
        self.out_wav.setframerate(self.out_rate)
        self.out_wav.setnchannels(self.in_wav.getnchannels())
        self.out_wav.setsampwidth (self.in_wav.getsampwidth())
        self.out_wav.setnframes(1)

        print("Nr output channels: %d" % self.out_wav.getnchannels())

        audio = self.in_wav.readframes(self.in_nframes)
        nroutsamples = round(len(audio) * self.out_rate/self.in_rate)
        print("Nr output samples: %d" %  nroutsamples)

        audio_out = sps.resample(np.fromstring(audio, self.nptype), nroutsamples)
        audio_out = audio_out.astype(self.nptype)

        self.out_wav.writeframes(audio_out.copy(order='C'))

        self.out_wav.close()

def main():
    ds = DownSample()
    if not ds.open_file(inwave): return 1
    ds.resample(outwave)
    return 0

if __name__ == '__main__':
    main()
2
jcoppens

信号をダウンサンプリング(または decimate )するには(サンプリングレートを下げることを意味します)、またはアップサンプリング(サンプリングレートを上げる)するには、データ間を補間する必要があります。

アイデアは、どういうわけか、ポイント間でカーブをdrawし、新しいサンプリングレートでこのカーブから値を取得する必要があるということです。これは、サンプリングされなかったある時点での音波の値を知りたいためです。そのため、何らかの方法でこの値を推測する必要があります。サブサンプリングが簡単になる唯一のケースは、サンプリングレートを整数$ k $で除算する場合です。この場合、$ k $サンプルのバケットを取得し、最初のサンプルのみを保持する必要があります。しかし、これはあなたの質問に答えません。 2つの異なるスケールでカーブをサンプリングした下の図を参照してください。

Signal

原理を理解していれば手動で行うこともできますが、ライブラリを使用することを強くお勧めします。その理由は、補間正しい方法は簡単ではないか、明白ではないためです。

線形補間(ラインでポイントを接続)または二項補間(多項式で3ポイントを接続)を使用したり、(場合によっては音に最適な)フーリエ変換を使用して周波数の空間で補間したりできます。フーリエ変換は手動で書き直す必要がないため、適切なサブサンプリング/サブサンプリングが必要な場合は、scipyとは異なるアルゴリズムを使用した2つのアップサンプリングの曲線について、次の図を参照してください。 「リサンプリング」機能はフーリエ変換を使用します。 Resampling scipy

確かに、44100Hzのウェーブファイルを読み込んでいて、48000Hzのサンプルデータが必要な場合は、データを読み込むために次の数行を書きました。

    # Imports
    from scipy.io import wavfile
    import scipy.signal as sps

    # Your new sampling rate
    new_rate = 48000

    # Read file
    sampling_rate, data = wavfile.read(path)

    # Resample data
    number_of_samples = round(len(data) * float(new_rate) / sampling_rate))
    data = sps.resample(data, number_of_samples)

ダウンサンプリングのみを実行していて、フーリエよりも高速なものが必要な場合は、メソッド decimate を使用することもできます。

1
Jeremy Cochoy

Librosaを使用してみましたが、y, s = librosa.load('test.wav', sr=16000)およびlibrosa.output.write_wav(filename, y, sr)という行を指定した後でも、サウンドファイルが指定のサンプルレート(16000、44kHzからダウンサンプリング)で保存されていません。しかし、pydubはうまく機能します。 jiaaroによる素晴らしいライブラリで、次のコマンドを使用しました。

from pydub import AudioSegment as am
sound = am.from_file(filepath, format='wav', frame_rate=22050)
sound = sound.set_frame_rate(16000)
sound.export(filepath, format='wav')

上記のコードは、22050のframe_rateで読み取るファイルが16000のレートに変更され、export関数がこのファイルで既存のファイルを新しいframe_rateで上書きすることを示しています。 librosaよりもうまく機能しますが、2つのパッケージの速度を比較する方法を探していますが、データが非常に少ないため、まだ分かりません。

参照: https://github.com/jiaaro/pydub/issues/232

1
Gowtham S