1#!/usr/bin/python 2 3# Audio Tools, a module and set of tools for manipulating audio data 4# Copyright (C) 2007-2014 Brian Langenberger 5 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 20import unittest 21import audiotools 22import tempfile 23import os 24import os.path 25from hashlib import md5 26import random 27import decimal 28import test_streams 29import subprocess 30try: 31 from configparser import SafeConfigParser 32except ImportError: 33 from ConfigParser import SafeConfigParser 34 35parser = SafeConfigParser() 36parser.read("test.cfg") 37 38 39def do_nothing(self): 40 pass 41 42 43# add a bunch of decorator metafunctions like LIB_CORE 44# which can be wrapped around individual tests as needed 45for section in parser.sections(): 46 for option in parser.options(section): 47 if parser.getboolean(section, option): 48 vars()["%s_%s" % (section.upper(), 49 option.upper())] = lambda function: function 50 else: 51 vars()["%s_%s" % (section.upper(), 52 option.upper())] = lambda function: do_nothing 53 54 55def BLANK_PCM_Reader(length, sample_rate=44100, channels=2, 56 bits_per_sample=16, channel_mask=None): 57 from audiotools.decoders import SameSample 58 59 if channel_mask is None: 60 channel_mask = int(audiotools.ChannelMask.from_channels(channels)) 61 62 return SameSample(sample=1, 63 total_pcm_frames=length * sample_rate, 64 sample_rate=sample_rate, 65 channels=channels, 66 channel_mask=channel_mask, 67 bits_per_sample=bits_per_sample) 68 69 70class EXACT_RANDOM_PCM_Reader(object): 71 def __init__(self, pcm_frames, 72 sample_rate=44100, channels=2, bits_per_sample=16, 73 channel_mask=None): 74 self.sample_rate = sample_rate 75 self.channels = channels 76 if channel_mask is None: 77 self.channel_mask = \ 78 int(audiotools.ChannelMask.from_channels(channels)) 79 else: 80 self.channel_mask = channel_mask 81 self.bits_per_sample = bits_per_sample 82 83 self.total_frames = pcm_frames 84 self.original_frames = self.total_frames 85 86 self.read = self.read_opened 87 88 def __enter__(self): 89 return self 90 91 def __exit__(self, exc_type, exc_value, traceback): 92 self.close() 93 94 def read_opened(self, pcm_frames): 95 if self.total_frames > 0: 96 frames_to_read = min(pcm_frames, self.total_frames) 97 frame = audiotools.pcm.FrameList( 98 os.urandom(frames_to_read * 99 (self.bits_per_sample // 8) * 100 self.channels), 101 self.channels, 102 self.bits_per_sample, 103 True, 104 True) 105 self.total_frames -= frame.frames 106 return frame 107 else: 108 return audiotools.pcm.empty_framelist( 109 self.channels, self.bits_per_sample) 110 111 def read_closed(self, pcm_frames): 112 raise ValueError("unable to read closed stream") 113 114 def close(self): 115 self.read = self.read_closed 116 117 def reset(self): 118 self.read = self.read_opened 119 self.total_frames = self.original_frames 120 121 122class RANDOM_PCM_Reader(EXACT_RANDOM_PCM_Reader): 123 def __init__(self, length, 124 sample_rate=44100, channels=2, bits_per_sample=16, 125 channel_mask=None): 126 EXACT_RANDOM_PCM_Reader.__init__( 127 self, 128 pcm_frames=length * sample_rate, 129 sample_rate=sample_rate, 130 channels=channels, 131 bits_per_sample=bits_per_sample, 132 channel_mask=channel_mask) 133 134 135def EXACT_BLANK_PCM_Reader(pcm_frames, sample_rate=44100, channels=2, 136 bits_per_sample=16, channel_mask=None): 137 from audiotools.decoders import SameSample 138 139 if channel_mask is None: 140 channel_mask = int(audiotools.ChannelMask.from_channels(channels)) 141 142 return SameSample(sample=1, 143 total_pcm_frames=pcm_frames, 144 sample_rate=sample_rate, 145 channels=channels, 146 channel_mask=channel_mask, 147 bits_per_sample=bits_per_sample) 148 149 150def EXACT_SILENCE_PCM_Reader(pcm_frames, sample_rate=44100, channels=2, 151 bits_per_sample=16, channel_mask=None): 152 from audiotools.decoders import SameSample 153 154 if channel_mask is None: 155 channel_mask = int(audiotools.ChannelMask.from_channels(channels)) 156 157 return SameSample(sample=0, 158 total_pcm_frames=pcm_frames, 159 sample_rate=sample_rate, 160 channels=channels, 161 channel_mask=channel_mask, 162 bits_per_sample=bits_per_sample) 163 164 165class MD5_Reader(audiotools.PCMReader): 166 def __init__(self, pcmreader): 167 audiotools.PCMReader.__init__( 168 self, 169 sample_rate=pcmreader.sample_rate, 170 channels=pcmreader.channels, 171 channel_mask=pcmreader.channel_mask, 172 bits_per_sample=pcmreader.bits_per_sample) 173 self.pcmreader = pcmreader 174 self.md5 = md5() 175 176 def __repr__(self): 177 return "MD5Reader(%s,%s,%s)" % (self.sample_rate, 178 self.channels, 179 self.bits_per_sample) 180 181 def reset(self): 182 if hasattr(self.pcmreader, "reset"): 183 self.pcmreader.reset() 184 self.md5 = md5() 185 186 def read(self, pcm_frames): 187 framelist = self.pcmreader.read(pcm_frames) 188 self.md5.update(framelist.to_bytes(False, True)) 189 return framelist 190 191 def close(self): 192 self.pcmreader.close() 193 194 def digest(self): 195 return self.md5.digest() 196 197 def hexdigest(self): 198 return self.md5.hexdigest() 199 200 201class Variable_Reader(audiotools.PCMReader): 202 def __init__(self, pcmreader): 203 audiotools.PCMReader.__init__( 204 self, 205 sample_rate=pcmreader.sample_rate, 206 channels=pcmreader.channels, 207 channel_mask=pcmreader.channel_mask, 208 bits_per_sample=pcmreader.bits_per_sample) 209 self.pcmreader = audiotools.BufferedPCMReader(pcmreader) 210 self.md5 = md5() 211 self.range = range(self.channels * (self.bits_per_sample // 8), 212 4096) 213 214 def read(self, pcm_frames): 215 return self.pcmreader.read(random.choice(self.range)) 216 217 def close(self): 218 self.pcmreader.close() 219 220 221class Join_Reader(audiotools.PCMReader): 222 # given a list of 1 channel PCM readers, 223 # combines them into a single reader 224 # a bit like PCMCat but across channels instead of PCM frames 225 def __init__(self, pcm_readers, channel_mask): 226 if len({r.sample_rate for r in pcm_readers}) != 1: 227 raise ValueError("all readers must have the same sample rate") 228 if len({r.bits_per_sample for r in pcm_readers}) != 1: 229 raise ValueError("all readers must have the same bits per sample") 230 if {r.channels for r in pcm_readers} != {1}: 231 raise ValueError("all readers must be 1 channel") 232 assert(isinstance(channel_mask, int)) 233 234 audiotools.PCMReader.__init__( 235 self, 236 sample_rate=pcm_readers[0].sample_rate, 237 channels=len(pcm_readers), 238 channel_mask=channel_mask, 239 bits_per_sample=pcm_readers[0].bits_per_sample) 240 241 self.pcm_readers = pcm_readers 242 self.readers = map(audiotools.BufferedPCMReader, pcm_readers) 243 244 def read(self, pcm_frames): 245 return audiotools.pcm.from_channels( 246 [r.read(pcm_frames) for r in self.pcm_readers]) 247 248 def reset(self): 249 for r in self.pcm_readers: 250 r.reset() 251 self.readers = map(audiotools.BufferedPCMReader, self.pcm_readers) 252 253 def close(self): 254 for r in self.pcm_readers: 255 r.close() 256 257 258class FrameCounter: 259 def __init__(self, channels, bits_per_sample, sample_rate, value=0): 260 self.channels = channels 261 self.bits_per_sample = bits_per_sample 262 self.sample_rate = sample_rate 263 self.value = value 264 265 def __repr__(self): 266 return "FrameCounter(%d %d %d %d)" % \ 267 (self.channels, 268 self.bits_per_sample, 269 self.sample_rate, 270 self.value) 271 272 def update(self, f): 273 self.value += len(f) 274 275 def __int__(self): 276 return int(round(decimal.Decimal(self.value) / 277 (self.channels * 278 (self.bits_per_sample // 8) * 279 self.sample_rate))) 280 281 282# probstat does this better, but I don't want to require that 283# for something used only rarely 284def Combinations(items, n): 285 if n == 0: 286 yield [] 287 else: 288 for i in range(len(items)): 289 for combos in Combinations(items[i + 1:], n - 1): 290 yield [items[i]] + combos 291 292 293def Possibilities(*lists): 294 if len(lists) == 0: 295 yield () 296 else: 297 remainder = list(Possibilities(*lists[1:])) 298 for item in lists[0]: 299 for rem in remainder: 300 yield (item,) + rem 301 302 303from_channels = audiotools.ChannelMask.from_channels 304 305# these are combinations that tend to occur in nature 306SHORT_PCM_COMBINATIONS = \ 307 ((11025, 1, int(from_channels(1)), 8), 308 (22050, 1, int(from_channels(1)), 8), 309 (22050, 1, int(from_channels(1)), 16), 310 (32000, 2, int(from_channels(2)), 16), 311 (44100, 1, int(from_channels(1)), 16), 312 (44100, 2, int(from_channels(2)), 16), 313 (48000, 1, int(from_channels(1)), 16), 314 (48000, 2, int(from_channels(2)), 16), 315 (48000, 6, int(audiotools.ChannelMask.from_fields( 316 front_left=True, 317 front_right=True, 318 front_center=True, 319 low_frequency=True, 320 back_left=True, 321 back_right=True)), 16), 322 (192000, 2, int(from_channels(2)), 24), 323 (96000, 6, int(audiotools.ChannelMask.from_fields( 324 front_left=True, 325 front_right=True, 326 front_center=True, 327 low_frequency=True, 328 back_left=True, 329 back_right=True)), 24)) 330 331 332TEST_COVER1 = open("test_cover1.jpg", "rb").read() 333 334TEST_COVER2 = open("test_cover2.png", "rb").read() 335 336TEST_COVER3 = open("test_cover3.jpg", "rb").read() 337 338TEST_COVER4 = open("test_cover4.png", "rb").read() 339 340# this is a very large, plain BMP encoded as bz2 341HUGE_BMP = open("huge.bmp.bz2", "rb").read() 342 343 344from test_formats import * 345from test_core import * 346from test_metadata import * 347from test_utils import * 348 349if (__name__ == '__main__'): 350 unittest.main() 351