2010-06-17 18 views

Répondre

12

Le module wave de la bibliothèque standard est la clé: après bien sûr import wave en haut de votre code, wave.open('the.wav', 'r') retourne un objet « vague lire » à partir de laquelle vous pouvez lire des cadres avec la méthode .readframes, qui renvoie une chaîne de octets qui sont les échantillons ... quel que soit le format du fichier wave (vous pouvez déterminer les deux paramètres pertinents pour décomposer les images en échantillons avec la méthode .getnchannels pour le nombre de canaux, et .getsampwidth pour le nombre d'octets par échantillon).

La meilleure façon de tourner la chaîne d'octets en une séquence de valeurs numériques est avec le module array, et un type de (respectivement) 'B', 'H', 'L' pour 1, 2, 4 octets par échantillon (sur une 32 -bit build de Python, vous pouvez utiliser la valeur itemsize de votre objet tableau pour vérifier cela). Si vous avez des largeurs d'échantillons différentes de celles que array peut vous fournir, vous devrez découper la chaîne d'octets (en remplissant chaque petite tranche avec des octets valant 0) et utiliser le module struct à la place (mais plus clunkieur et plus lent, utilisez array à la place si tu peux).

+0

quand j'essaye .getsamplewidth il m'a donné une valeur 2 signifiant que 2 octets .. quand j'essaye .readframes (1) devrait retourner 1 image puis il est retourné pour moi comme "/ x03/x16" qui je suppose est de 2 octets, donc cela signifie que 1 trame a seulement 1 échantillon .. ce qui est l'utilisation getnchannels ?? je veux prendre des échantillons de chaque image séparement et les représenter dans les intergers, comment puis-je ?? – kaki

+1

@kaki, dans chaque trame, il y a le premier échantillon de chaque canal, puis le deuxième échantillon de chaque canal, puis ainsi de suite. Donc, à moins que votre son soit mono, c'est-à-dire juste un canal, vous devez décider quoi faire avec les canaux (passez tous sauf un, faites la moyenne, peu importe). Disons que c'est 1 canal (mono), le plus simple, puis 'x = array.array ('h', w.getframes (1))' vous donne dans 'x' un tableau avec tous les samples de la première image (next, si dans une boucle) comme des entiers, tout comme vous dites que vous voulez ('h', pas' H': ils sont signés). Si stéréo, 2 canaux, même les indices de «x» ont par exemple. échantillons de canal gauche. Little-endian btw. –

+0

BTW, le format docs à https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ n'utilise pas le concept de "cadres" mais plutôt "morceaux" et "sous-morceaux", mais à la fin, il vient à peu près la même chose bien sûr ;-). –

2

Vous pouvez utiliser le module wave. Vous devez d'abord lire les métadonnées, telles que la taille de l'échantillon ou le nombre de chaînes. En utilisant la méthode readframes(), vous pouvez lire des échantillons, mais uniquement sous la forme d'une chaîne d'octets. Basé sur le format de l'échantillon, vous devez les convertir en échantillons en utilisant struct.unpack(). Vous pouvez également utiliser le module io.wavfile de SciPy si vous souhaitez que les échantillons soient un tableau de nombres à virgule flottante.

+0

pouvez-vous me dire comment obtenir l'échantillon comme un tableau de nombres point flottant sans utiliser scipy – kaki

2

est ici une fonction pour lire les échantillons d'un fichier wave (testé avec stéréo mono &):

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() 

Et voici le script complet qui ne mélange ou de plusieurs concaténation fenêtré fichiers .wav. Tous les fichiers d'entrée doivent avoir les mêmes paramètres (nombre de canaux et largeur d'échantillon).

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

Après avoir lu les échantillons (par exemple avec le module d'onde, plus de détails here), vous pouvez avoir les valeurs échelles entre -1 et 1 (ce qui est la convention pour les signaux audio).

Dans ce cas, vous pouvez ajouter:

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

avec nb_bits la profondeur de bits et signal_int les valeurs des nombres entiers.