problem with rewriting a wav file in python - python
I have a problem with rewriting a .wav file (wave audio file). my project involves converting a wave file data into bytes of data and then reassembling a new audio file which sounds the same.
For some reason, when I try to do this with struct.pack, the result is similar, but not the same - it seems like the original data but not exactly.
note: for normal string it works, but for the type of data in which wave files are written, it doesn't.
My function for converting the original data to bytes:
def original_data_to_bytes_data(original_data):
"""
params: original data.
returns: all the data in bytes form, list of strings.
"""
original_data = str(''.join(format(ord(i), '08b') for i in original_data))
bytes_data = list()
for i in range(0, len(original_data), 8):
bytes_data.append(original_data[i:i+8])
return bytes_data
My function for converting the bytes to the original data:
def bytes_data_to_original_data(bytes_data):
"""
params: bytes_data - data, list of strings.
returns: original data.
"""
original_data =""
for i in bytes_data:
original_data += struct.pack('i', int(i, 2))
return original_data
Thanks for the help!
On Python 3 I get error message. On Python 2 it works without error so I assume that you also use Python 2.
I checked this
data = 'A'
result = bytes_data_to_original_data(original_data_to_bytes_data(data))
print(result)
print(type(data), type(result))
and it display the same text and the same typu
But when I check
print(data == result)
print(len(data), len(result))
print(repr(data), repr(result))
then it show that data and result are different
False
(1, 4)
("'A'", "'A\\x00\\x00\\x00'")
If I use "B" (byte) instead of "i" (integer) in code
struct.pack('B', int(i, 2))
then I get the same values - so wave should sound the same too.
it works also if I use bytes b"A" instead of string "A" because Python2 tread bytes as string.
def bytes_data_to_original_data(bytes_data):
"""
params: bytes_data - data, list of strings.
returns: original data.
"""
original_data = ""
for i in bytes_data:
original_data += struct.pack('B', int(i, 2))
return original_data
EDIT: In struct.pack() I changed 'b' (which need values -128..127) to 'B' (which works with values 0..255).
Related
How to fix this IO bound python operation on 12GB .bin file?
I'm reading this book Hands-On Machine Learning for Algorithmic Trading and I came across a script that is supposed to parse a large .bin binary file and convert it to .h5. This file consists of something called ITCH data, you can find the technical documentation of the data here. The script is very inefficient, it reads a 12GB(12952050754 bytes) file 2 bytes at a time which is ultra slow(might take up to 4 hours on some decent 4cpu GCP instance) which is not very surprising. You can find the whole notebook here. My problem is I don't understand how this .bin file is being read I mean I don't see where is the necessity of reading the file 2 bytes at a time, I think there is a way to read at a large buffer size but I'm not sure how to do it, or even convert the script to c++ if after optimizing this script, it is still being slow which I can do if I understand the inner workings of this I/O process, does anyone have suggestions? here's a link to the file source of ITCH data, you can find small files(300 mb or less) which are for less time periods if you need to experiment with the code. The bottleneck: with file_name.open('rb') as data: while True: # determine message size in bytes message_size = int.from_bytes(data.read(2), byteorder='big', signed=False) # get message type by reading first byte message_type = data.read(1).decode('ascii') message_type_counter.update([message_type]) # read & store message record = data.read(message_size - 1) message = message_fields[message_type]._make(unpack(fstring[message_type], record)) messages[message_type].append(message) # deal with system events if message_type == 'S': seconds = int.from_bytes(message.timestamp, byteorder='big') * 1e-9 print('\n', event_codes.get(message.event_code.decode('ascii'), 'Error')) print(f'\t{format_time(seconds)}\t{message_count:12,.0f}') if message.event_code.decode('ascii') == 'C': store_messages(messages) break message_count += 1 if message_count % 2.5e7 == 0: seconds = int.from_bytes(message.timestamp, byteorder='big') * 1e-9 d = format_time(time() - start) print(f'\t{format_time(seconds)}\t{message_count:12,.0f}\t{d}') res = store_messages(messages) if res == 1: print(pd.Series(dict(message_type_counter)).sort_values()) break messages.clear() And here's the store_messages() function: def store_messages(m): """Handle occasional storing of all messages""" with pd.HDFStore(itch_store) as store: for mtype, data in m.items(): # convert to DataFrame data = pd.DataFrame(data) # parse timestamp info data.timestamp = data.timestamp.apply(int.from_bytes, byteorder='big') data.timestamp = pd.to_timedelta(data.timestamp) # apply alpha formatting if mtype in alpha_formats.keys(): data = format_alpha(mtype, data) s = alpha_length.get(mtype) if s: s = {c: s.get(c) for c in data.columns} dc = ['stock_locate'] if m == 'R': dc.append('stock') try: store.append(mtype, data, format='t', min_itemsize=s, data_columns=dc) except Exception as e: print(e) print(mtype) print(data.info()) print(pd.Series(list(m.keys())).value_counts()) data.to_csv('data.csv', index=False) return 1 return 0
According to the code, file format looks like its 2 bytes of message size, one byte of message type and then n bytes of actual message (defined by the previously read message size). Low hanging fruit to optimize this is to read 3 bytes first into list, convert [0:1] to message size int and [2] to message type and then read the message .. To further eliminate amount of required reads, you could read a fixed amount of data from the file into a list of and start extracting from it. While extracting, keep a index of already processed bytes stored and once that index or index+amount of data to be read goes over the size of the list, you prepopulate the list. This could lead to huge memory requirements if not done properly thought..
Why python has different types of bytes
I have two variables, one is b_d, the other is b_test_d. When I type b_d in the console, it shows: b'\\\x8f\xc2\xf5(\\\xf3?Nb\x10X9\xb4\x07#\x00\x00\x00\x00\x00\x00\xf0?' when I type b_test_d in the console, it shows: b'[-2.1997713216,-1.4249271187,-1.1076795391,1.5224958034,-0.1709796203,0.3663875698,0.14846441,-0.7415930061,-1.7602231949,0.126605689,0.6010934792,-0.466415358,1.5675525816,1.00836295,1.4332792992,0.6113384254,-1.8008540571,-0.9443408896,1.0943670356,-1.0114642686,1.443892627,-0.2709427287,0.2990462512,0.4650133591,0.2560791327,0.2257600462,-2.4077429827,-0.0509983213,1.0062187148,0.4315075795,-0.6116110033,0.3495131413,-0.3249903375,0.3962305931,-0.1985757285,1.165792433,-1.1171953063,-0.1732557874,-0.3791600654,-0.2860519953,0.7872658859,0.217728374,-0.4715179983,-0.4539613811,-0.396353657,1.2326862425,-1.3548659354,1.6476230786,0.6312713442,-0.735444661,-0.6853447369,-0.8480631975,0.9538606574,0.6653542368,-0.2833696021,0.7281604648,-0.2843872095,0.1461980484,-2.3511731773,-0.3118047948,-1.6938613893,-0.0359659687,-0.5162134311,-2.2026641552,-0.7294895084,0.7493073213,0.1034096968,0.6439803068,-0.2596155272,0.5851323455,1.0173285542,-0.7370464113,1.0442954406,-0.5363832595,0.0117795359,0.2225617514,0.067571974,-0.9154681906,-0.293808596,1.3717113798,0.4919516922,-0.3254944005,1.6203744532,-0.1810222279,-0.6111596457,1.344064259,-0.4596893179,-0.2356197144,0.4529942046,1.6244603294,0.1849995925,0.6223061217,-0.0340662398,0.8365900535,-0.6804201929,0.0149665385,0.4132453788,0.7971962667,-1.9391525531,0.1440486871,-0.7103617816,0.9026539637,0.6665798363,-1.5885073458,1.4084493329,-1.397040825,1.6215697667,1.7057148522,0.3802647045,-0.4239271483,1.4773614536,1.6841461329,0.1166845529,-0.3268795898,-0.9612751672,0.4062399443,0.357209662,-0.2977362702,-0.3988147401,-0.1174652196,0.3350589818,-1.8800423584,0.0124169787,1.0015110265,0.789541751,-0.2710408983,1.4987300181,-1.1726824468,-0.355322591,0.6567978423,0.8319110558,0.8258835069,-1.1567887763,1.9568551122,1.5148655075,1.0589021915,-0.4388232953,-0.7451680183,-2.1897621693,0.4502135234,-1.9583089063,0.1358789518,-1.7585860897,0.452259777,0.7406800349,-1.3578980418,1.108740204,-1.1986272667,-1.0273598206,-1.8165822264,1.0853600894,-0.273943514,0.8589890805,1.3639094329,-0.6121993589,-0.0587067992,0.0798457584,1.0992814648,-1.0455733611,1.4780003064,0.5047157705,0.1565451605,0.9656886956,-0.5998330255,0.4846727299,0.8790524818,1.0288893846,-2.0842447397,0.4074607421,2.1523241756,-1.1268047125,-0.6016001524,-1.3302141561,1.1869516954,1.0988060125,0.7405900405,1.1813110811,0.8685330644,2.0927140519,-1.7171952009,0.9231993147,0.320874115,0.7465845079,-0.1034484959,-0.4776822499,0.436218328,-0.4083564542,0.4835567895,1.0733230373,-0.858658902,-0.4493571034,0.4506418221,1.6696649735,-0.9189799982,-1.1690356499,-1.0689397924,0.3174297583,1.0403701444,0.5440082812,-0.1128248996]' Both of them are bytes type, but I can use numpy.frombuffer to read the b_d, but not the b_test_d. And they look very different. Why do I have these two types of bytes? Thank you.
[A]nyone can point out how to use Json marshall to convert the byte to the same type of bytes as the first one? This isn't the right question, but I think I know what you're asking. You say you're getting the 2nd array via JSON marshalling, but that it's also not under your control: it was obtained by json marshal (convert a received float array to byte array, and then convert the result to base64 string, which is done by someone else) That's fine though, you just have to do a few steps of processing to get to a state equivalent to the first set of bytes. First, some context to what's going on. You've already seen that numpy can understand your first set of bytes. >>> numpy.frombuffer(data) [1.21 2.963 1. ] Based on its output, it looks like numpy is interpreting your data as 3 doubles, with 8 bytes each (24 bytes total)... >>> data = b'\\\x8f\xc2\xf5(\\\xf3?Nb\x10X9\xb4\x07#\x00\x00\x00\x00\x00\x00\xf0?' >>> len(data) 24 ...which the struct module can also interpret. # Separate into 3 doubles x, y, z = data[:8], data[8:16], data[16:] print([struct.unpack('d', i) for i in (x, y, z)]) [(1.21,), (2.963,), (1.0,) There's actually (at least) 2 ways you can get a numpy array out of this. Short way 1. Convert to string # Original JSON data (snipped) junk = b'[-2.1997713216,-1.4249271187,-1.1076795391,...]' # Decode from bytes to a string (defaults to utf-8), then # trim off the brackets (first and last characters in the string) as_str = junk.decode()[1:-1] 2. Use numpy.fromstring numpy.fromstring(as_str, dtype=float, sep=',') # Produces: array([-2.19977132, -1.42492712, -1.10767954, 1.5224958 , -0.17097962, 0.36638757, 0.14846441, -0.74159301, -1.76022319, 0.12660569, 0.60109348, -0.46641536, 1.56755258, 1.00836295, 1.4332793 , 0.61133843, -1.80085406, -0.94434089, 1.09436704, -1.01146427, 1.44389263, -0.27094273, 0.29904625, 0.46501336, 0.25607913, 0.22576005, -2.40774298, -0.05099832, 1.00621871, 0.43150758, ... ]) Long way Note: I found the fromstring method after writing this part up, figured I'd leave it here to at least help explain the byte differences. 1. Convert the JSON data into an array of numeric values. # Original JSON data (snipped) junk = b'[-2.1997713216,-1.4249271187,-1.1076795391,...]' # Decode from bytes to a string - defaults to utf-8 junk = junk.decode() # Trim off the brackets - First and last characters in the string junk = junk[1:-1] # Separate into values junk = junk.split(',') # Convert to numerical values doubles = [float(val) for val in junk] # Or, as a one-liner doubles = [float(val) for val in junk.decode()[1:-1].split(',')] # "doubles" currently holds: [-2.1997713216, -1.4249271187, -1.1076795391, 1.5224958034, ...] 2. Use struct to get byte-representations for the doubles import struct as_bytes = [struct.pack('d', val) for val in doubles] # "as_bytes" currently holds: [b'\x08\x9b\xe7\xb4!\x99\x01\xc0', b'\x0b\x00\xe0`\x80\xcc\xf6\xbf', b'+ ..\x0e\xb9\xf1\xbf', b'hg>\x8f$\\\xf8?', ...] 3. Join all the double values (as bytes) into a single byte-string, then submit to numpy new_data = b''.join(as_bytes) numpy.frombuffer(new_data) # Produces: array([-2.19977132, -1.42492712, -1.10767954, 1.5224958 , -0.17097962, 0.36638757, 0.14846441, -0.74159301, -1.76022319, 0.12660569, 0.60109348, -0.46641536, 1.56755258, 1.00836295, 1.4332793 , 0.61133843, -1.80085406, -0.94434089, 1.09436704, -1.01146427, 1.44389263, -0.27094273, 0.29904625, 0.46501336, 0.25607913, 0.22576005, -2.40774298, -0.05099832, 1.00621871, 0.43150758, ... ])
A bytes object can be in any format. It is "just a bunch of bytes" without context. For display Python will represent byte values <128 as their ASCII value, and use hex escape codes (\x##) for others. The first looks like IEEE 754 double precision floating point. numpy or struct can read it. The second one is in JSON format. Use the json module to read it: import numpy as np import json import struct b1 = b'\\\x8f\xc2\xf5(\\\xf3?Nb\x10X9\xb4\x07#\x00\x00\x00\x00\x00\x00\xf0?' b2 = b'[-2.1997713216,-1.4249271187,-1.1076795391,1.5224958034]' j = json.loads(b2) n = np.frombuffer(b1) s = struct.unpack('3d',b1) print(j,n,s,sep='\n') # To convert b2 into a b1 format b = struct.pack('4d',*j) print(b) Output: [-2.1997713216, -1.4249271187, -1.1076795391, 1.5224958034] [1.21 2.963 1. ] (1.21, 2.963, 1.0) b'\x08\x9b\xe7\xb4!\x99\x01\xc0\x0b\x00\xe0`\x80\xcc\xf6\xbf+ ..\x0e\xb9\xf1\xbfhg>\x8f$\\\xf8?'
unpacking pythons struct.pack in another language
I want to "unpack" OR de-serialize the formatted data that is outputed from python's struct.pack() function. The data is sent over the network to another platform that uses Java only. The Python function that sends data over the network, uses this formater: def formatOutputMsg_Array(self, mac, arr): mac_bin = mac.encode("ascii"); mac_len = len(mac_bin); arr_bin = array.array('d', arr).tobytes(); arr_len = len(arr_bin); m = struct.pack('qqd%ss%ss' % (mac_len, arr_len), mac_len, arr_len, time.time(), mac_bin, arr_bin); return m Here are the docs for python's struct (refer to section 7.3.2.2. Format Characters): https://docs.python.org/2/library/struct.html 1) The issue is what does 'qqd%ss%ss' mean ??? Does it mean -> long,long,double,char,char,[],char[],char,char[],char[] 2) why is modulo "%" used here with a tuple 'qqd%ss%ss' % (mac_len, arr_len) ?
The first argument to pack is the result of the expression 'qqd%ss%ss' % (mac_len, arr_len), where the two %s are replaced by the values of the given variables. Assuming mac_len == 8 and arr_len == 4, for example, the result is qqd8s4s. s preceded by a number simply means to copy the given bytes for that format into the result.
python convert bit hex to binary
ok, im fairly new to python but not programming, I know php, C, bash, etc... My question is: How do I convert data = "b'\x16'" to binary "0001 0110" ?? im trying to read the response from an esc printer from DLE x = 1 while x: time.sleep(3) ser.write("\x10\x04\x01".encode()) bytesToRead = ser.inWaiting() data = ser.read(bytesToRead) while data: print(data) data = "" all that ends up printing is: b'\x16' i assume hex but a simple hex to bin is not working because of the b?
What you get back is a bytes object. (think: raw array of bytes) You can get the number itself from the first byte via data[0]. That will give you 0x16 as an int, which you can convert however you want.
Convert string to ndarray in python
I am reading a stream of data from an A-D converter via a socket from Python; the data come in as raw bytes. I want to format these bytes as int32 and place them into an ndarray. The read process looks something like this: def datarecv(): global msgbuf binlen = BURSTLEN + 4 while len(msgbuf) < binlen: msgbuf = msgbuf + socket.recv(4096) reply = msgbuf[0:binlen] msgbuf = msgbuf[binlen:] # each recv comes with a 4 byte header that I throw away... return reply[4:] The following is used successfully to write the received data to a file: with open(filename, "wb') as f: bytesremaining = framesize for i in range(lines): f.write(datarecv()[0:min(linesize, bytesremaining)]) bytesremaining -= linesize I can then read back the file with something like this: >>> data = numpy.fromfile(filename, dtype='int32') >>> type(data) <type 'numpy.ndarray'> So my data variable is the format I'm looking for, I.E. >>> data[1:10] array([4214234234, 2342342342, 2342342342, 34534535, 345345353, 5675757, 2142423424, 35334535, 35353535, 4754745754], dtype=int32) ** BUT ** I want to omit the intermediate step of writing to a file. After I read in the raw stream of data I want to make it an ndarray so that I can manipulate the data. I can change the line from f.write(datarecv()[0:min(linesize, bytesremaining)]) to bigbuf = bigbuf + datarecv()[0:min(linesize, bytesremaining)] and then I end up with a big string. It's a string of raw bytes (not ASCII) which I have to convert to 32 bit integers. I'm hung up on this last step. I hope this makes sense what I'm asking. Thanks.
You can convert bigbuf to an array with numpy.fromstring For example: In [21]: bigbuf = "\1\0\0\0\2\0\0\0" In [22]: fromstring(bigbuf, dtype=np.int32) Out[22]: array([1, 2], dtype=int32)