2010-06-17 15 views

Respuesta

12

El módulo wave de la biblioteca estándar es la clave: después por supuesto import wave en la parte superior de su código, wave.open('the.wav', 'r') devuelve un objeto "Wave leer" desde la que se puede leer marcos con el método .readframes, que devuelve una cadena de bytes que son las muestras ... en cualquier formato que el archivo de onda las tenga (puede determinar los dos parámetros relevantes para descomponer cuadros en muestras con el método .getnchannels para el número de canales, y .getsampwidth para el número de bytes por muestra).

La mejor manera de convertir la cadena de bytes en una secuencia de valores numéricos es con el módulo de array, y un tipo de (respectivamente) 'B', 'H', 'L' para 1, 2, 4 bytes por muestra (en un 32 -bit compilación de Python; puede usar el valor itemsize de su objeto array para verificarlo dos veces). Si tiene diferentes anchos de muestra que array puede proporcionarle, deberá cortar la cadena de bytes (rellenando cada pequeña porción apropiadamente con bytes equivalentes a 0) y usar el módulo struct (pero eso es más lento y más lento, entonces use array en su lugar si puedes).

+0

cuando intento .getsamplewidth me dio un valor 2 que significa que 2 bytes ... cuando intento .readframes (1) debería devolver 1 fotograma, luego volvió para mí, como "/ x03/x16" , que supongo que es de 2 bytes, por lo que significa que 1 fotograma tiene solo 1 muestra ... ¿para qué sirve getnchannels? quiero tomar muestras de cada foto por separado y representarlas en intergers, ¿cómo puedo? – kaki

+1

@kaki, en cada cuadro, aparece la primera muestra de cada canal, luego la segunda muestra de cada canal, y así sucesivamente. Entonces, a menos que su sonido sea mono, es decir, solo 1 canal, debe decidir qué hacer con los canales (omita todos menos uno, promedielos, lo que sea). Digamos que es 1 canal (mono), lo más simple, luego 'x = array.array ('h', w.getframes (1))' te da en 'x' una matriz con todas las muestras del primer cuadro (siguiente, si está en un bucle) como números enteros, tal como dices que quieres ('h', no' H': están firmados). Si estéreo, 2 canales, incluso índices de 'x 'tienen, p. muestras del canal izquierdo. Little-Endian por cierto. –

+0

BTW, los documentos de formato en https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ no usan el concepto de "marcos" sino más bien "trozos" y "subcamas", pero al final se trata de a casi lo mismo, por supuesto ;-). –

2

Puede usar el módulo wave. Primero debe leer los metadatos, como el tamaño de muestra o la cantidad de canales. Con el método readframes(), puede leer muestras, pero solo como una cadena de bytes. Según el formato de muestra, debe convertirlos en muestras usando struct.unpack().

Alternativamente, si quiere las muestras como una matriz de números de coma flotante, puede usar el módulo io.wavfile de SciPy.

+0

¿Puede decirme cómo obtener la muestra como una matriz de números de punto floatinf sin usar scipy – kaki

2

Aquí hay una función para leer las muestras de un archivo de onda (probado con mono & estéreo):

def read_samples(wave_file, nb_frames): 
    frame_data = wave_file.readframes(nb_frames) 
    if frame_data: 
     sample_width = wave_file.getsampwidth() 
     nb_samples = len(frame_data) // sample_width 
     format = {1:"%db", 2:"<%dh", 4:"<%dl"}[sample_width] % nb_samples 
     return struct.unpack(format, frame_data) 
    else: 
     return() 

Y aquí está el guión completo que no Windowed mezcla o concatenación de múltiples archivos .wav. Todos los archivos de entrada deben tener los mismos parámetros (número de canales y ancho de muestra).

import argparse 
import itertools 
import struct 
import sys 
import wave 

def _struct_format(sample_width, nb_samples): 
    return {1:"%db", 2:"<%dh", 4:"<%dl"}[sample_width] % nb_samples 

def _mix_samples(samples): 
    return sum(samples)//len(samples) 

def read_samples(wave_file, nb_frames): 
    frame_data = wave_file.readframes(nb_frames) 
    if frame_data: 
     sample_width = wave_file.getsampwidth() 
     nb_samples = len(frame_data) // sample_width 
     format = _struct_format(sample_width, nb_samples) 
     return struct.unpack(format, frame_data) 
    else: 
     return() 

def write_samples(wave_file, samples, sample_width): 
    format = _struct_format(sample_width, len(samples)) 
    frame_data = struct.pack(format, *samples) 
    wave_file.writeframes(frame_data) 

def compatible_input_wave_files(input_wave_files): 
    nchannels, sampwidth, framerate, nframes, comptype, compname = input_wave_files[0].getparams() 
    for input_wave_file in input_wave_files[1:]: 
     nc,sw,fr,nf,ct,cn = input_wave_file.getparams() 
     if (nc,sw,fr,ct,cn) != (nchannels, sampwidth, framerate, comptype, compname): 
      return False 
    return True 

def mix_wave_files(output_wave_file, input_wave_files, buffer_size): 
    output_wave_file.setparams(input_wave_files[0].getparams()) 
    sampwidth = input_wave_files[0].getsampwidth() 
    max_nb_frames = max([input_wave_file.getnframes() for input_wave_file in input_wave_files]) 
    for frame_window in xrange(max_nb_frames // buffer_size + 1): 
     all_samples = [read_samples(wave_file, buffer_size) for wave_file in input_wave_files] 
     mixed_samples = [_mix_samples(samples) for samples in itertools.izip_longest(*all_samples, fillvalue=0)] 
     write_samples(output_wave_file, mixed_samples, sampwidth) 

def concatenate_wave_files(output_wave_file, input_wave_files, buffer_size): 
    output_wave_file.setparams(input_wave_files[0].getparams()) 
    sampwidth = input_wave_files[0].getsampwidth() 
    for input_wave_file in input_wave_files: 
     nb_frames = input_wave_file.getnframes() 
     for frame_window in xrange(nb_frames // buffer_size + 1): 
      samples = read_samples(input_wave_file, buffer_size) 
      if samples: 
       write_samples(output_wave_file, samples, sampwidth) 

def argument_parser(): 
    parser = argparse.ArgumentParser(description='Mix or concatenate multiple .wav files') 
    parser.add_argument('command', choices = ("mix", "concat"), help='command') 
    parser.add_argument('output_file', help='ouput .wav file') 
    parser.add_argument('input_files', metavar="input_file", help='input .wav files', nargs="+") 
    parser.add_argument('--buffer_size', type=int, help='nb of frames to read per iteration', default=1000) 
    return parser 

if __name__ == '__main__': 
    args = argument_parser().parse_args() 

    input_wave_files = [wave.open(name,"rb") for name in args.input_files] 
    if not compatible_input_wave_files(input_wave_files): 
     print "ERROR: mixed wave files must have the same params." 
     sys.exit(2) 

    output_wave_file = wave.open(args.output_file, "wb") 
    if args.command == "mix": 
     mix_wave_files(output_wave_file, input_wave_files, args.buffer_size) 
    elif args.command == "concat": 
     concatenate_wave_files(output_wave_file, input_wave_files, args.buffer_size) 

    output_wave_file.close() 
    for input_wave_file in input_wave_files: 
     input_wave_file.close() 
0

Después de leer las muestras (por ejemplo con el módulo de onda, más detalles here) es posible que desee tener los valores escamas entre -1 y 1 (esta es la convención para señales de audio).

En este caso, se puede añadir:

# scale to -1.0 -- 1.0 
max_nb_bit = float(2**(nb_bits-1)) 
samples = signal_int/(max_nb_bit + 1.0) 

con nb_bits la profundidad de bits y signal_int los valores enteros.

Cuestiones relacionadas