source: ammosreader/AmmosAudioReader.py@ 6cb85c2

AmmosSource guix
Last change on this file since 6cb85c2 was 6cb85c2, checked in by Enrico Schwass <ennoausberlin@…>, 3 years ago

documentation added and numerical constants replaced

  • Property mode set to 100644
File size: 7.4 KB
RevLine 
[6cb85c2]1"""I parse an R&S AMMOS recording."""
2
[1e781ba]3import os
4
5from AmmosGlobalFrameBody import AmmosGlobalFrameBody
6from AmmosAudioDataHeader import AmmosAudioDataHeader
7from AmmosExtendedAudioDataHeader import AmmosExtendedAudioDataHeader
8from AmmosGlobalFrameHeader import AmmosGlobalFrameHeader
9from AmmosSingleFrame import AmmosSingleFrame
10from AmmosContainer import AmmosContainer
11
12
13class AmmosAudioReader():
[6cb85c2]14 """I read the audio data embedded in an R&S AMMOS recording."""
15
16 GLOBAL_HEADER_SIZE = 24 # 8 words
17 STANDARD_AUDIO_DATA_HEADER_SIZE = 36 # 9 words
18 EXTENDED_AUDIO_DATA_HEADER_SIZE = 44 # 11 words
[1e781ba]19
20 def __init__(self, file_name):
[6cb85c2]21 """
22 I return an instance of AmmosAudioReader initialized with a given file name.
[1e781ba]23
[6cb85c2]24 :param file_name: the file to read from
25 :type file_name: str
26 """
[1e781ba]27 self.file_name = file_name
28 self.file = open(self.file_name, "rb")
29 self.file_size = os.path.getsize(self.file_name)
30
31 self.container = AmmosContainer(self.file_name, [])
32
[6cb85c2]33 self.tags = {}
[1e781ba]34
35 def rewind_to_start(self):
[6cb85c2]36 """I set the file pointer to the beginning of the file for the next operation."""
[1e781ba]37 self.file.seek(0)
38
39 def add_tag(self, tag):
[6cb85c2]40 """
41 I add a tag to my tag list.
42
43 :param tag: The tag to add to my tag list
44 :type tag: dict
45 """
46 self.tags[tag.key] = tag.value
[1e781ba]47
48 def read_all_frames_left(self):
[6cb85c2]49 """
50 I read all remaining frames into my container until end of file is reached.
[1e781ba]51
[6cb85c2]52 :return: a container containing all frames read
53 :rtype: AmmosContainer
54 """
[1e781ba]55 frames_read = 0
56 while True:
57 print("Reading single frame", frames_read, '...')
58 current_frame = self.read_next_single_frame()
59 if current_frame is not None:
60 frames_read += 1
61 self.container.add_frame(current_frame)
62 if frames_read % 10000 == 0:
63 print("#", end="")
64 else:
65 print("Frame:", frames_read+1, " incomplete")
66 break
67
68 print(len(self.container.global_frames), "frames read")
[6cb85c2]69 return self.container
[1e781ba]70
71 def read_next_global_frame_header(self):
[6cb85c2]72 """
73 I return the next global frame header read from current position in file.
74
75 :return: the next global frame header or None if incomplete
76 :rtype: AmmosGlobalFrameHeader
77 """
78 bytes = self.file.read(AmmosAudioReader.GLOBAL_HEADER_SIZE)
[1e781ba]79 print("Reading next global frame header")
[6cb85c2]80 if ((not bytes) or (len(bytes) < AmmosAudioReader.GLOBAL_HEADER_SIZE)):
81 print("Can not read all", AmmosAudioReader.GLOBAL_HEADER_SIZE, "bytes of global frame header")
[1e781ba]82 return None
83
[6cb85c2]84 # FIXME: Catch exceptions and add some asserts
[1e781ba]85 current_global_frame_header = AmmosGlobalFrameHeader.from_bytes(bytes)
[6cb85c2]86 # print("Current global frame header", current_global_frame_header)
[1e781ba]87 return current_global_frame_header
88
89 def read_next_global_frame_body_data_header(self):
[6cb85c2]90 """
91 I return the next global frame body data header from current position in file.
[1e781ba]92
[6cb85c2]93 :param data_header_size: the number of bytes to read
94 :type data_header_size: int
95 :return: the next Ammos Audio Data header or None if incomplete
96 :rtype: AmmosAudioDataHeader
97 """
98 bytes = self.file.read(AmmosAudioReader.STANDARD_AUDIO_DATA_HEADER_SIZE)
[1e781ba]99
100 # print("\nReading global frame body standard data header\n")
[6cb85c2]101 if ((not bytes) or (len(bytes) < AmmosAudioReader.STANDARD_AUDIO_DATA_HEADER_SIZE)):
102 print("Can not read all", AmmosAudioReader.STANDARD_AUDIO_DATA_HEADER_SIZE,
103 "bytes of global frame body data header")
[1e781ba]104 return None
[6cb85c2]105 return AmmosAudioDataHeader.from_bytes(bytes)
[1e781ba]106
107 def read_next_global_frame_body_extended_data_header(self):
[6cb85c2]108 """
109 I return the next global frame body extended data header from current position in file.
[1e781ba]110
[6cb85c2]111 :return: the next Ammos Audio Extended Data header or None if incomplete
112 :rtype: AmmosExtendedAudioDataHeader
113 """
114 bytes = self.file.read(AmmosAudioReader.EXTENDED_AUDIO_DATA_HEADER_SIZE)
[1e781ba]115
[6cb85c2]116 if ((not bytes) or (len(bytes) < AmmosAudioReader.EXTENDED_AUDIO_DATA_HEADER_SIZE)):
117 print("Can not read all ", AmmosAudioReader.EXTENDED_AUDIO_DATA_HEADER_SIZE,
118 " bytes of global frame extended data header")
[1e781ba]119 return None
[6cb85c2]120 return AmmosExtendedAudioDataHeader.from_bytes(bytes)
[1e781ba]121
122 def read_next_audio_data_body(self, sample_count, channel_count, sample_size):
[6cb85c2]123 """
124 I return the next audio data read from current position in file.
[1e781ba]125
[6cb85c2]126 :param sample_count: the number of samples per channel inside data body
127 :type sample_count: int
128
129 :param channel_count: number of channels (e.g. mono, stereo or even more)
130 :type channel_count: int
131
132 :param sample_size: sample size in bytes (1, 2 or 4 bytes)
133 :type sample_size: int
134
135 :return: the next audio data or None if incomplete
136 :rtype: bytes
137 """
[1e781ba]138 # FIXME: Describe the parameters better
139
140 total = sample_count*channel_count*sample_size
141
142 byte_string = self.file.read(total)
143
144 if len(byte_string) != total:
145 print("Can not read all", total, "bytes of data body")
146 return None
147 print([hex(c) for c in byte_string])
148 return byte_string
149
150 def read_next_global_frame_body(self, global_frame_header):
151
152 audio_data_header = None
153
[6cb85c2]154 if global_frame_header.data_header_length == AmmosAudioReader.STANDARD_AUDIO_DATA_HEADER_SIZE:
[1e781ba]155 print("Read standard data header")
156 audio_data_header = self.read_next_global_frame_body_data_header()
157
[6cb85c2]158 if global_frame_header.data_header_length == AmmosAudioReader.EXTENDED_AUDIO_DATA_HEADER_SIZE:
[1e781ba]159 print("Read extended data header")
160 audio_data_header = self.read_next_global_frame_body_extended_data_header()
161
162 if audio_data_header is None:
[6cb85c2]163 print("Data header missing or format unknown")
[1e781ba]164 return None
165
166 audio_data_body = self.read_next_audio_data_body(audio_data_header.sample_count,
167 audio_data_header.channel_count,
168 audio_data_header.sample_size)
169
170 if audio_data_body is None:
171 print("Data body missing")
172 return None
173
174 return AmmosGlobalFrameBody(audio_data_header, audio_data_body)
175
176 def read_next_single_frame(self):
177
178 global_frame_header = self.read_next_global_frame_header()
179
[6cb85c2]180 print(global_frame_header)
181
[1e781ba]182 if global_frame_header is None:
183 print("Global frame header missing")
184 return None
185
186 if global_frame_header.data_header_length is None:
187 print("Data header length empty")
188 return None
189
190 if global_frame_header.frame_type == 256:
191 print("Audio Datastream found")
192 global_frame_body = self.read_next_global_frame_body(global_frame_header)
193 if global_frame_body is None:
194 return None
195 else:
196 print("Unsupported frame type", global_frame_header.frame_type, "found")
197 return None
198
199 ammos_single_frame = AmmosSingleFrame(global_frame_header, global_frame_body)
200 return ammos_single_frame
Note: See TracBrowser for help on using the repository browser.