Taztingo
Taztingo

Reputation: 1945

Binary string to wav file

I'm trying to write a wav upload function for my webapp. The front end portion seems to be working great. The problem is my backend (python). When it receives the binary data I'm not sure how to write it to a file. I tried using the basic write functon, and the sound is corrupt... Sounds like "gobbly-gook". Is there a special way to write wav files in Python?

Here is my backend... Not really much to it.

form = cgi.FieldStorage()
fileData = str(form.getvalue('data'))

with open("audio", 'w') as file:
    file.write(fileData)

I even tried...

with open("audio", 'wb') as file:
    file.write(fileData)

I am using aplay to play the sound, and I noticed that all the properties are messed up as well.

Before: Signed 16 bit Little Endian, Rate 44100 Hz, Stereo

After upload: Unsigned 8 bit, Rate 8000 Hz, Mono

Upvotes: 0

Views: 11205

Answers (2)

Taztingo
Taztingo

Reputation: 1945

The code I pasted will write a wav file as long as the data isn't corrupt. It was not necessary to use the wave module.

with open("audio", 'w') as file:
    file.write(fileData)

I was originally reading the file in Javascript as FileAPI.readAsBinaryString. I changed this to FileAPI.readAsDataURL, and then decoded it in python using base64.decode(). Once I decoded it I was able to just write the data to a file. The .wav file was in perfect condition.

Upvotes: 0

dogwynn
dogwynn

Reputation: 408

Perhaps the wave module might help?

import wave
import struct
import numpy as np

rate = 44100

def sine_samples(freq, dur):
    # Get (sample rate * duration) samples on X axis (between freq
    # occilations of 2pi)
    X = (2*np.pi*freq/rate) * np.arange(rate*dur)

    # Get sine values for these X axis samples (as integers)
    S = (32767*np.sin(X)).astype(int)

    # Pack integers as signed "short" integers (-32767 to 32767)
    as_packed_bytes = (map(lambda v:struct.pack('h',v), S))
    return as_packed_bytes

def output_wave(path, frames):
    # Python 3.X allows the use of the with statement
    # with wave.open(path,'w') as output:
    #     # Set parameters for output WAV file
    #     output.setparams((2,2,rate,0,'NONE','not compressed'))
    #     output.writeframes(frames)

    output = wave.open(path,'w')
    output.setparams((2,2,rate,0,'NONE','not compressed'))
    output.writeframes(frames)
    output.close()

def output_sound(path, freq, dur):
    # join the packed bytes into a single bytes frame
    frames = b''.join(sine_samples(freq,dur))

    # output frames to file
    output_wave(path, frames)

output_sound('sine440.wav', 440, 2)

EDIT:

I think in your case, you might only need:

packedData = map(lambda v:struct.pack('h',v), fileData)
frames = b''.join(packedData)
output_wave('example.wav', frames)

In this case, you just need to know the sampling rate. Check the wave module for information on the other output file parameters (i.e. the arguments to the setparams method).

Upvotes: 4

Related Questions