¿Cómo escribo un archivo WAV de 24 bits en Python?

Quiero generar un archivo de audio de formato WAV de 24 bits utilizando Python 2.7 a partir de una matriz de valores de punto flotante entre -1 y 1. No puedo usar scipy.io.wavfile.write porque solo admite 16 o 32 bits. La documentación del propio módulo wave de Python no especifica qué formato de datos toma.

Entonces, ¿es posible hacer esto en Python?

Otra opción está disponible en wavio (también en PyPI: https://pypi.python.org/pypi/wavio ), un pequeño módulo que creé para solucionar el problema de scipy que aún no admite archivos WAV de 24 bits. El archivo wavio.py contiene la función de write , que escribe una matriz numpy en un archivo WAV. Para escribir un archivo de 24 bits, use el argumento sampwidth=3 . La única dependencia de wavio es numpy; wavio utiliza la wave biblioteca estándar para tratar con el formato de archivo WAV.

Por ejemplo,

 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) 

Ya presenté una respuesta a esta pregunta hace 2 años, donde recomendé scikits.audiolab .

Mientras tanto, la situación ha cambiado y ahora hay una biblioteca disponible que es mucho más fácil de usar y mucho más fácil de instalar, incluso viene con su propia copia de la biblioteca libsndfile para Windows y OSX (en Linux es fácil de instalar de todos modos ): PySoundFile !

Si tiene CFFI y NumPy instalados, puede instalar PySoundFile simplemente ejecutando

 pip install soundfile --user 

Escribir un archivo WAV de 24 bits es fácil:

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

En este ejemplo, my_audio_data debe ser una matriz NumPy con el dtype 'float64' , 'float32' , 'int32' o 'int16' .

Por cierto, hice una página de resumen donde intenté comparar muchas bibliotecas de Python disponibles para leer / escribir archivos de sonido.

Prueba el módulo 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 solo puede empaquetar números enteros en tamaños de 2 y 4 bits. Así que puedes usar una matriz numpy con un dtype en int32, y usar una lista de comprensión para obtener 3/4 de los bytes de cada entero:

 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'] 

Usando el módulo de wave , la función Wave_write.writeframes espera que los datos WAV se empaqueten en una cadena de 3 bytes en formato little-endian. El siguiente código hace el truco:

 import wave from contextlib import closing import struct def wavwrite_24(fname, fs, data): data_as_bytes = (struct.pack(' 

Debes probar 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 

Y para leerlo de nuevo:

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

scikits.audiolab usa libsndfile , así que además de los archivos WAV, también puede usar FLAC, OGG y algunos formatos de archivo más.

Aquí hay una versión actualizada de scipy.io.wavfile que agrega:

  • Soporte de archivos .wav de 24 bits para lectura / escritura,
  • acceso a marcadores de cue,
  • tags de marcadores de cue,
  • algunos otros metadatos como el tono (si está definido), etc.

wavfile.py (mejorado)

Utilice ffmpeg para intercambiar entre códecs wav, a continuación se muestra un código de ejemplo

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