簡體   English   中英

如何在 Python 中編寫 24 位 WAV 文件?

[英]How do I write a 24-bit WAV file in Python?

我想使用 Python 2.7 從 -1 到 1 之間的浮點值數組生成一個 24 位 WAV 格式的音頻文件。我不能使用scipy.io.wavfile.write因為它只支持 16 位或 32 位。 Python 自己的wave模塊的文檔沒有指定它采用什么格式的數據。

那么有可能在 Python 中做到這一點嗎?

2 年前我已經提交了這個問題的答案,我推薦了 scikits.audiolab

與此同時,情況發生了變化,現在有一個更易於使用且更易於安裝的庫,它甚至帶有自己的用於 Windows 和 OSX 的libsndfile庫副本(在 Linux 上無論如何都易於安裝): PySoundFile

如果您安裝了 CFFI 和 NumPy,您只需運行即可安裝 PySoundFile

pip install soundfile --user

編寫 24 位 WAV 文件很容易:

import soundfile as sf
sf.write('my_24bit_file.wav', my_audio_data, 44100, 'PCM_24')

在此示例中, my_audio_data必須是具有dtype 'float64''float32''int32''int16'的 NumPy 數組。

順便說一句,我創建了一個概述頁面,嘗試比較許多可用的 Python 庫以讀取/寫入聲音文件。

使用wave模塊, Wave_write.writeframes函數期望 WAV 數據以 little-endian 格式打包成一個 3 字節的字符串。 以下代碼可以解決問題:

import wave
from contextlib import closing
import struct

def wavwrite_24(fname, fs, data):
    data_as_bytes = (struct.pack('<i', int(samp*(2**23-1))) for samp in data)
    with closing(wave.open(fname, 'wb')) as wavwriter:
        wavwriter.setnchannels(1)
        wavwriter.setsampwidth(3)
        wavwriter.setframerate(fs)
        for data_bytes in data_as_bytes:
            wavwriter.writeframes(data_bytes[0:3])

試試wave模塊:

In [1]: import wave

In [2]: w = wave.open('foo.wav', 'w') # open for writing

In [3]: w.setsampwidth(3) # 3 bytes/sample

Python 只能打包 2 和 4 位大小的整數。 因此,您可以在 int32 上使用具有 dtype 的 numpy 數組,並使用列表推導來獲取每個整數的 3/4 字節:

In [14]: d = np.array([1,2,3,4], dtype=np.int32)

In [15]: d
Out[15]: array([1, 2, 3, 4], dtype=int32)

In [16]: [d.data[i:i+3] for i in range(0,len(d)*d.dtype.itemsize, d.dtype.itemsize)]
Out[16]: ['\x01\x00\x00', '\x02\x00\x00', '\x03\x00\x00', '\x04\x00\x00']

wavio中提供了另一個選項(也在 PyPI 上: https ://pypi.python.org/pypi/wavio),這是我創建的一個小模塊,用於解決 scipy 尚不支持 24 位 WAV 文件的問題。 文件wavio.py包含函數write ,它將一個 numpy 數組寫入 WAV 文件。 要寫入 24 位文件,請使用參數sampwidth=3 wavio的唯一依賴是 numpy; wavio使用標准庫wave來處理 WAV 文件格式。

例如,

In [21]: import numpy as np

In [22]: import wavio

In [23]: rate = 22050             # samples per second

In [24]: T = 3                    # sample duration (seconds)

In [25]: f = 440.0                # sound frequency (Hz)

In [26]: t = np.linspace(0, T, T*rate, endpoint=False)

In [27]: sig = np.sin(2 * np.pi * f * t)

In [28]: wavio.write("sine24.wav", sig, rate, sampwidth=3)

您應該嘗試scikits.audiolab

import numpy as np
from scikits.audiolab import Sndfile, Format

sig = np.array([0, 1, 0, -1, 0], dtype=np.float32)
f = Sndfile('test_pcm24.wav', 'w', Format('wav', 'pcm24'), 1, 44100)
f.write_frames(sig)
f.close()  # use contextlib.closing in real code

再讀一遍:

f = Sndfile('test_pcm24.wav')
sig = f.read_frames(f.nframes, dtype=np.float32)
f.close()  # use contextlib.closing in real code

scikits.audiolab使用libsndfile ,因此除了 WAV 文件,您還可以使用 FLAC、OGG 和更多文件格式。

這是scipy.io.wavfile的更新版本,它添加了:

  • 24 位 .wav 文件支持讀/寫,
  • 訪問提示標記,
  • 提示標記標簽,
  • 其他一些元數據,如音高(如果已定義)等。

wavfile.py(增強)

利用 ffmpeg 在 wav 編解碼器之間進行交換,下面是示例代碼

command = "ffmpeg -i input.wav -ar 22050 output.wav"
subprocess.call(command, shell=True)

@detly 的解決方案效果很好。

為每個樣本幀調用一次writeframes帶來巨大的開銷,並使原始解決方案非常慢。 計算到數組並在單個調用中寫入數據可能會產生更好的性能。

這就是我正在使用的:

import wave
from contextlib import closing
import struct
import numpy as np

INT24_FAC = (2**23)-1

def wavwrite_24(filename, fs, data):
    data_as_bytes = np.array(list(struct.pack('<i', x)[0:3] for x in (INT24_FAC * data).astype(int)))
    with closing(wave.open(filename, 'wb')) as wavwriter:
        wavwriter.setnchannels(1)
        wavwriter.setsampwidth(3)
        wavwriter.setframerate(fs)
        wavwriter.writeframes(data_as_bytes)

在我的情況下,輸入數據是一個有效的 numpy 數組,如果這有什么不同的話。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM