2010-02-09 17 views
6

Sto provando a scrivere un programma per visualizzare i dati PCM. Sono stato molto frustrato nel cercare di trovare una libreria con il giusto livello di astrazione, ma ho trovato la libreria wave Python e l'ho usata. Tuttavia, non sono sicuro di come interpretare i dati.Interpretazione dei dati WAV

La funzione wave.getparams restituisce (2 canali, 2 byte, 44100 Hz, 96333 frame, Nessuna compressione, Nessuna compressione). Sembra tutto allegro, ma poi ho provato a stampare un singolo frame: '\ xc0 \ xff \ xd0 \ xff' che è 4 byte. Suppongo che sia possibile che un frame sia 2 campioni, ma le ambiguità non finiscono qui.

96333 fotogrammi * 2 campioni/telaio * (1/44.1k sec/campione) = 4.3688 secondi

Tuttavia, iTunes riporta il tempo come più vicino a 2 secondi e calcoli in base alle dimensioni del file e bitrate sono in campo da baseball di 2,7 secondi. Cosa sta succedendo qui?

Inoltre, come faccio a sapere se i byte sono firmate o senza segno?

Grazie mille!

risposta

8

"Due canali" significa stereo, quindi non ha senso somma durata di ogni canale - quindi sei fuori di un fattore due (2,18 secondi, non 4,37). Per quanto riguarda signedness, come spiegato per esempio here, e cito:

8-bit campioni vengono memorizzati come unsigned byte, che va da 0 a 255. 16 bit campioni vengono memorizzati come 2's-complemento firmato interi, che vanno da -32768 a 32767.

Questo fa parte delle specifiche del formato WAV (in realtà del suo RIFF superset) e quindi non dipende da ciò che si sta utilizzando biblioteca a che fare con un file WAV .

+0

Grazie! Posso solo sperare che sia stata la mia mancanza di sonno a impedirmi di notare il numero stereo ;-) – SapphireSun

2

Ogni campione è 16 bit e là 2 canali, in modo che il telaio richiede 4 byte

2

La durata è semplicemente il numero di frame diviso per il numero di fotogrammi al secondo. Dai tuoi dati questo è: 96333/44100 = 2.18 seconds.

4

So che una risposta è già stata accettata, ma ho fatto alcune cose con l'audio un po 'di tempo fa e devi decomprimere l'onda facendo qualcosa di simile.

pcmdata = wave.struct.unpack("%dh"%(wavedatalength),wavedata) 

Inoltre, un pacchetto che ho usato è stato chiamato PyAudio, se ancora dovuto usare il pacchetto onda con esso.

17

Grazie per il vostro aiuto! Ho ottenuto che funziona e vi posterò la soluzione qui da usare per chiunque nel caso in cui qualche altra povera anima ha bisogno:

import wave 
import struct 

def pcm_channels(wave_file): 
    """Given a file-like object or file path representing a wave file, 
    decompose it into its constituent PCM data streams. 

    Input: A file like object or file path 
    Output: A list of lists of integers representing the PCM coded data stream channels 
     and the sample rate of the channels (mixed rate channels not supported) 
    """ 
    stream = wave.open(wave_file,"rb") 

    num_channels = stream.getnchannels() 
    sample_rate = stream.getframerate() 
    sample_width = stream.getsampwidth() 
    num_frames = stream.getnframes() 

    raw_data = stream.readframes(num_frames) # Returns byte data 
    stream.close() 

    total_samples = num_frames * num_channels 

    if sample_width == 1: 
     fmt = "%iB" % total_samples # read unsigned chars 
    elif sample_width == 2: 
     fmt = "%ih" % total_samples # read signed 2 byte shorts 
    else: 
     raise ValueError("Only supports 8 and 16 bit audio formats.") 

    integer_data = struct.unpack(fmt, raw_data) 
    del raw_data # Keep memory tidy (who knows how big it might be) 

    channels = [ [] for time in range(num_channels) ] 

    for index, value in enumerate(integer_data): 
     bucket = index % num_channels 
     channels[bucket].append(value) 

    return channels, sample_rate 
1

Basandosi sulle this answer, è possibile ottenere un buon incremento delle prestazioni utilizzando numpy.fromstring o numpy.fromfile. Vedi anche this answer.

Ecco quello che ho fatto:

def interpret_wav(raw_bytes, n_frames, n_channels, sample_width, interleaved = True): 

    if sample_width == 1: 
     dtype = np.uint8 # unsigned char 
    elif sample_width == 2: 
     dtype = np.int16 # signed 2-byte short 
    else: 
     raise ValueError("Only supports 8 and 16 bit audio formats.") 

    channels = np.fromstring(raw_bytes, dtype=dtype) 

    if interleaved: 
     # channels are interleaved, i.e. sample N of channel M follows sample N of channel M-1 in raw data 
     channels.shape = (n_frames, n_channels) 
     channels = channels.T 
    else: 
     # channels are not interleaved. All samples from channel M occur before all samples from channel M-1 
     channels.shape = (n_channels, n_frames) 

    return channels 

Assegnazione di un nuovo valore per modellare genera un errore se si richiede i dati da copiare in memoria.Questa è una buona cosa, dal momento che si desidera utilizzare i dati in posizione (utilizzando meno tempo e memoria in generale). Anche la funzione ndarray.T non copia (ad esempio restituisce una vista) se possibile, ma non sono sicuro del modo in cui lo assicura che non venga copiato.

La lettura direttamente dal file con np.fromfile sarà ancora migliore, ma si dovrebbe saltare l'intestazione usando un dtype personalizzato. Non ho ancora provato questo.

Problemi correlati