2010-06-17 36 views
5

Tôi muốn biết cách lấy mẫu ra khỏi tệp .wav để thực hiện tham gia cửa sổ của hai tệp .wav.cách lấy mẫu wav từ tệp wav?

Bạn có thể cho biết cách thực hiện việc này không?

Trả lời

12

Module wave của thư viện chuẩn là chìa khóa: sau khi tất nhiên import wave ở phía trên cùng của mã của bạn, wave.open('the.wav', 'r') trả về một "làn sóng đọc" đối tượng mà từ đó bạn có thể đọc khung với phương pháp .readframes, mà trả về một chuỗi các byte là các mẫu ... trong bất kỳ định dạng nào mà tệp sóng có chúng (bạn có thể xác định hai thông số có liên quan để phân tách khung thành các mẫu với phương pháp .getnchannels cho số kênh và .getsampwidth cho số byte cho mỗi mẫu).

Cách tốt nhất để biến các chuỗi các byte vào một chuỗi các giá trị số là với các module array, và một loại (tương ứng) 'B', 'H', 'L' cho 1, 2, 4 byte cho mỗi mẫu (trên 32 -bit xây dựng của Python, bạn có thể sử dụng giá trị itemsize của đối tượng mảng của bạn để kiểm tra lại điều này). Nếu bạn có độ rộng mẫu khác nhau hơn array có thể cung cấp cho bạn, bạn sẽ cần phải cắt chuỗi byte (đệm từng lát nhỏ một cách thích hợp với byte có giá trị 0) và sử dụng mô-đun struct thay thế (nhưng đó là clunkier và chậm hơn, vì vậy hãy sử dụng array để thay thế nếu bạn có thể).

+0

khi tôi thử .getamplewidth nó đã cho tôi một giá trị 2 có nghĩa là 2 byte .. khi tôi thử .readframes (1) 1 khung sau đó nó trở lại cho tôi như "/ x03/x16" mà tôi đoán là 2 byte, do đó, nó có nghĩa là 1 khung chỉ có 1 mẫu .. những gì được sử dụng getnchannels ?? tôi muốn lấy mẫu từ mỗi khung tách biệt và đại diện cho chúng trong bộ ghi, làm thế nào tôi có thể ?? – kaki

+1

@kaki, trong mỗi khung hình, có mẫu đầu tiên từ mỗi kênh, sau đó là mẫu thứ hai từ mỗi kênh, v.v. Vì vậy, trừ khi âm thanh của bạn là mono, tức là chỉ 1 kênh bạn phải quyết định làm gì trên kênh (bỏ qua tất cả trừ một kênh, trung bình chúng, bất kỳ thứ gì). Giả sử nó là 1 kênh (mono), đơn giản nhất, sau đó 'x = array.array ('h', w.getframes (1))' cung cấp cho bạn trong 'x' một mảng với tất cả các mẫu của khung đầu tiên (tiếp theo, nếu trong một vòng lặp) là số nguyên, giống như bạn nói bạn muốn ('h', không phải' H': chúng được ký). Nếu âm thanh nổi, 2 kênh, ngay cả chỉ mục của 'x' đều có, ví dụ: mẫu kênh trái. Little-endian btw. –

+0

BTW, các tài liệu định dạng tại https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ không sử dụng khái niệm "khung" mà là "khối" và "phần con", nhưng cuối cùng nó đến với nhiều điều tương tự tất nhiên ;-). –

2

Bạn có thể sử dụng mô-đun wave. Trước tiên, bạn nên đọc siêu dữ liệu, chẳng hạn như kích thước mẫu hoặc số lượng kênh. Sử dụng phương thức readframes(), bạn có thể đọc các mẫu, nhưng chỉ như một chuỗi byte. Dựa trên định dạng mẫu, bạn phải chuyển đổi chúng thành các mẫu bằng cách sử dụng struct.unpack().

Hoặc, nếu bạn muốn các mẫu dưới dạng một mảng các số dấu phẩy động, bạn có thể sử dụng mô-đun io.wavfile của SciPy.

+0

u có thể cho tôi biết làm thế nào để lấy mẫu như một mảng số điểm floatinf mà không sử dụng scipy – kaki

2

Dưới đây là một chức năng để đọc mẫu từ một tập tin sóng (thử nghiệm với mono & stereo):

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

Và đây là kịch bản đầy đủ mà không cửa sổ trộn hoặc concatenating nhiều .wav tập tin. Tất cả các tệp đầu vào cần phải có cùng thông số (số kênh và chiều rộng mẫu).

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

Sau khi đọc các mẫu (ví dụ với các module sóng, biết thêm chi tiết here), bạn có thể muốn có các giá trị quy mô giữa -1 và 1 (đây là quy ước cho tín hiệu âm thanh).

Trong trường hợp này, bạn có thể thêm:

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

với nb_bits độ sâu bit và signal_int các số nguyên giá trị.

Các vấn đề liên quan