2015-02-17 19 views
5

Questo mi ha causato un giorno di mal di testa, ma poiché l'ho capito, ho voluto postarlo da qualche parte nel caso fosse utile.Scrivi array numpy per generare il file wave nei buffer utilizzando il modulo wave (non scipy.io.wavfile)

Sto usando il modulo wave di python per scrivere dati in un file wave. NON sto usando scipy.io.wavfile perché i dati possono essere un vettore enorme (ore di audio a 16kHz) che non voglio/non posso caricare in memoria tutto in una volta. La mia comprensione è che scipy.io.wavfile fornisce solo un'interfaccia a file intero, mentre wave può consentire di leggere e scrivere nei buffer. Mi piacerebbe essere corretto su questo se sbaglio.

Il problema che stavo eseguendo si riduce a come convertire i dati float in byte per la funzione wave.writeframes. I miei dati non sono stati scritti nell'ordine corretto. Questo perché ero utilizzando la funzione numpy.getbuffer() per convertire i dati in byte, che non rispetta l'orientamento dei dati:

x0 = np.array([[0,1],[2,3],[4,5]],dtype='int8') 
x1 = np.array([[0,2,4],[1,3,5]],dtype='int8').transpose() 
if np.array_equal(x0, x1): 
    print "Data are equal" 
else: 
    print "Data are not equal" 
b0 = np.getbuffer(x0) 
b1 = np.getbuffer(x1) 

risultato:

Data are equal 

In [453]: [b for b in b0] 
Out[453]: ['\x00', '\x01', '\x02', '\x03', '\x04', '\x05'] 

In [454]: [b for b in b1] 
Out[454]: ['\x00', '\x02', '\x04', '\x01', '\x03', '\x05'] 

I assumere l'ordine di byte è determinato dall'assegnazione iniziale in memoria, in quanto numpy.transpose() non riscrive i dati ma restituisce solo una vista. Tuttavia dal momento che questo fatto è sepolto dall'interfaccia per gli array numpy, il debug di questo prima di sapere che questo era il problema era un doozy.

Una soluzione è quella di utilizzare tostring di NumPy() Funzione:

s0 = x0.tostring() 
s1 = x1.tostring() 
In [455]: s0 
Out[455]: '\x00\x01\x02\x03\x04\x05' 

In [456]: s1 
Out[456]: '\x00\x01\x02\x03\x04\x05' 

Questo è probabilmente evidente a chiunque dire la funzione toString() prima, ma in qualche modo la mia ricerca non scavare qualsiasi buona documentazione su come per formattare un intero array numpy per la scrittura di file wave diverso da usare scipy.io.wavfile. Quindi eccolo qui. Proprio per il completamento (notare che "caratteristiche" è originariamente n_channels x N_SAMPLES, che è il motivo per cui ho avuto questo problema al fine di dati per cominciare:.

outfile = wave.open(output_file, mode='w') 
outfile.setnchannels(features.shape[0]) 
outfile.setframerate(fs) 
outfile.setsampwidth(2) 
bytes = (features*(2**15-1)).astype('i2').transpose().tostring() 
outfile.writeframes(bytes) 
outfile.close() 
+1

Invece di 'tostring', è possibile utilizzare' struct.pack ', che consente di specificare l'endianità e le dimensioni. Consulta http://stackoverflow.com/a/19999599/120261 per un esempio di leggendo con 'struct.unpack', si spera che sarà un utile punto di partenza se si vuole provare in questo altro modo. – mtrw

+1

Prova '' np.getbuffer (x1.ravel()) ''. Inoltre, che dire dell'uso di '' x0.tofile() '' invece di '' x0.tostring() ''? Se sei preoccupato per Endianess leggi http://docs.scipy.org/doc/numpy/user/basics.byteswapping.html – Dietrich

+0

Ho dimenticato: quando crei array multidimensionali, puoi usare il parametro '' order'' per determinare il come sono scritti in memoria: http://docs.scipy.org/doc/numpy/reference/generated/numpy.array.html – Dietrich

risposta

1

Per me tostring funziona bene notare che in WAVE un file a 8 bit deve essere firmato, mentre altri (16 o 32 bit) deve essere firmato

Alcuni codice demo sporca che funziona per me:.

import wave 
import numpy as np 

SAMPLERATE=44100 
BITWIDTH=8 
CHANNELS=2 

def gensine(freq, dur): 
    t = np.linspace(0, dur, round(dur*SAMPLERATE)) 
    x = np.sin(2.0*np.pi*freq*t) 
    if BITWIDTH==8: 
     x = x+abs(min(x)) 
     x = np.array(np.round((x/max(x)) * 255) , dtype=np.dtype('<u1')) 
    else: 
     x = np.array(np.round(x * ((2**(BITWIDTH-1))-1)), dtype=np.dtype('<i%d' % (BITWIDTH/8))) 

    return np.repeat(x,CHANNELS).reshape((len(x),CHANNELS)) 

output_file="test.wav" 

outfile = wave.open(output_file, mode='wb') 
outfile.setparams((CHANNELS, BITWIDTH/8, SAMPLERATE, 0, 'NONE', 'not compressed')) 
outfile.writeframes(gensine(440, 1).tostring()) 
outfile.writeframes(gensine(880, 1).tostring()) 
outfile.close()