2011-10-20 59 views
20

Estoy buscando una forma de averiguar la duración de un archivo de audio (.wav) en python. Hasta ahora he echado un vistazo a la biblioteca python wave, mutagen, pymedia, pymad no pude obtener la duración del archivo wav. Pymad me dio la duración pero no es consistente.Obtener longitud o duración del archivo .wav

Gracias de antemano.

Respuesta

35

La duración es igual al número de tramas dividido por la tasa de fotogramas (cuadros por segundo):

import wave 
import contextlib 
fname = '/tmp/test.wav' 
with contextlib.closing(wave.open(fname,'r')) as f: 
    frames = f.getnframes() 
    rate = f.getframerate() 
    duration = frames/float(rate) 
    print(duration) 

respecto al comentario @edwards', aquí está algo de código para producir una onda de 2 canales file:

import math 
import wave 
import struct 
FILENAME = "/tmp/test.wav" 
freq = 440.0 
data_size = 40000 
frate = 1000.0 
amp = 64000.0 
nchannels = 2 
sampwidth = 2 
framerate = int(frate) 
nframes = data_size 
comptype = "NONE" 
compname = "not compressed" 
data = [(math.sin(2 * math.pi * freq * (x/frate)), 
     math.cos(2 * math.pi * freq * (x/frate))) for x in range(data_size)] 
try: 
    wav_file = wave.open(FILENAME, 'w') 
    wav_file.setparams(
     (nchannels, sampwidth, framerate, nframes, comptype, compname)) 
    for values in data: 
     for v in values: 
      wav_file.writeframes(struct.pack('h', int(v * amp/2))) 
finally: 
    wav_file.close() 

Si reproduce el archivo resultante en un reproductor de audio, verá que tiene una duración de 40 segundos. Si ejecuta el código anterior, también calcula que la duración es de 40 segundos. Por lo tanto, creo que el número de fotogramas no está influenciado por la cantidad de canales y que la fórmula anterior es correcta.

+0

tuve un vistazo a todo el 'wave' función de biblioteca pero se pasa por alto lógica simple' nframes/frame_rate'. Gracias por el método y el código :) – Pannu

+2

Esto no es del todo correcto ... hay un fotograma escrito para cada canal, y por lo tanto 'duración = frames/float (rate * f.getnchannels())' – edward

+1

@edward : He agregado un código arriba que crea un archivo de onda de 2 canales. La fórmula publicada en mi respuesta calcula la duración en 40 segundos, lo que concuerda con lo que veo cuando reproduzco el archivo .wav. Por lo tanto, me parece que el número de fotogramas no se duplica cuando usa 2 canales y mi fórmula original es correcta. – unutbu

5
import os 
path="c:\\windows\\system32\\loopymusic.wav" 
f=open(path,"r") 

#read the ByteRate field from file (see the Microsoft RIFF WAVE file format) 
#https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ 
#ByteRate is located at the first 28th byte 
f.seek(28) 
a=f.read(4) 

#convert string a into integer/longint value 
#a is little endian, so proper conversion is required 
byteRate=0 
for i in range(4): 
    byteRate=byteRate + ord(a[i])*pow(256,i) 

#get the file size in bytes 
fileSize=os.path.getsize(path) 

#the duration of the data, in milliseconds, is given by 
ms=((fileSize-44)*1000)/byteRate 

print "File duration in miliseconds : " % ms 
print "File duration in H,M,S,mS : " % ms/(3600*1000) % "," % ms/(60*1000) % "," % ms/1000 % "," ms%1000 
print "Actual sound data (in bytes) : " % fileSize-44 
f.close() 
+0

Un enfoque más seguro para tratar con los contenidos binarios del archivo sin el bucle byteRate podría ser: desde struct import unpack_from tasa, = unpack_from (' edrabc

+0

Pequeño error: 'os.path.getsize (ruta) 'debe ser' os.path.getsize (f) '. – Lewistrick

+0

Otro pequeño error: 'ms = ((fileSize-44) * 1000)/byteRate' Pero esto es excelente porque funciona incluso si su WAV no es PCM. – Jamie

5

podemos utilizar ffmpeg para obtener la duración de cualquier archivo de video o audio.

Para instalar ffmpeg siguen este método muy simple link

import subprocess 
import re 

process = subprocess.Popen(['ffmpeg', '-i', path_of_wav_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 
stdout, stderr = process.communicate() 
matches = re.search(r"Duration:\s{1}(?P<hours>\d+?):(?P<minutes>\d+?):(?P<seconds>\d+\.\d+?),", stdout, re.DOTALL).groupdict() 

print matches['hours'] 
print matches['minutes'] 
print matches['seconds'] 
+0

Recibí este error "no puedo usar un patrón de cadena en un objeto similar a un byte". Así que reemplacé la llamada a "stdout" con "stdout.decode()" – AvielNiego

1

Una es utilizar pysoundfile, https://github.com/bastibe/PySoundFile

Aquí hay un código de ejemplo de cómo hacer esto:

import soundfile as sf 
f = sf.SoundFile('447c040d.wav') 
print('samples = {}'.format(len(f))) 
print('sample rate = {}'.format(f.samplerate)) 
print('seconds = {}'.format(len(f)/f.samplerate)) 

La salida para ese archivo en particular es:

samples = 232569 
sample rate = 16000 
seconds = 14.5355625 

Esto se alinea con soxi:

Input File  : '447c040d.wav' 
Channels  : 1 
Sample Rate : 16000 
Precision  : 16-bit 
Duration  : 00:00:14.54 = 232569 samples ~ 1090.17 CDDA sectors 
File Size  : 465k 
Bit Rate  : 256k 
Sample Encoding: 16-bit Signed Integer PCM 
Cuestiones relacionadas