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
Line 
1"""I parse an R&S AMMOS recording."""
2
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():
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
19
20 def __init__(self, file_name):
21 """
22 I return an instance of AmmosAudioReader initialized with a given file name.
23
24 :param file_name: the file to read from
25 :type file_name: str
26 """
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
33 self.tags = {}
34
35 def rewind_to_start(self):
36 """I set the file pointer to the beginning of the file for the next operation."""
37 self.file.seek(0)
38
39 def add_tag(self, tag):
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
47
48 def read_all_frames_left(self):
49 """
50 I read all remaining frames into my container until end of file is reached.
51
52 :return: a container containing all frames read
53 :rtype: AmmosContainer
54 """
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")
69 return self.container
70
71 def read_next_global_frame_header(self):
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)
79 print("Reading next global frame header")
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")
82 return None
83
84 # FIXME: Catch exceptions and add some asserts
85 current_global_frame_header = AmmosGlobalFrameHeader.from_bytes(bytes)
86 # print("Current global frame header", current_global_frame_header)
87 return current_global_frame_header
88
89 def read_next_global_frame_body_data_header(self):
90 """
91 I return the next global frame body data header from current position in file.
92
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)
99
100 # print("\nReading global frame body standard data header\n")
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")
104 return None
105 return AmmosAudioDataHeader.from_bytes(bytes)
106
107 def read_next_global_frame_body_extended_data_header(self):
108 """
109 I return the next global frame body extended data header from current position in file.
110
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)
115
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")
119 return None
120 return AmmosExtendedAudioDataHeader.from_bytes(bytes)
121
122 def read_next_audio_data_body(self, sample_count, channel_count, sample_size):
123 """
124 I return the next audio data read from current position in file.
125
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 """
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
154 if global_frame_header.data_header_length == AmmosAudioReader.STANDARD_AUDIO_DATA_HEADER_SIZE:
155 print("Read standard data header")
156 audio_data_header = self.read_next_global_frame_body_data_header()
157
158 if global_frame_header.data_header_length == AmmosAudioReader.EXTENDED_AUDIO_DATA_HEADER_SIZE:
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:
163 print("Data header missing or format unknown")
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
180 print(global_frame_header)
181
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.