1# -*- coding: utf-8 -*- 2 3import os 4 5from mutagen._compat import cBytesIO 6from mutagen.aiff import AIFF, AIFFInfo, delete, IFFFile, IFFChunk 7from mutagen.aiff import error as AIFFError 8 9from tests import TestCase, DATA_DIR, get_temp_copy 10 11 12class TAIFF(TestCase): 13 silence_1 = os.path.join(DATA_DIR, '11k-1ch-2s-silence.aif') 14 silence_2 = os.path.join(DATA_DIR, '48k-2ch-s16-silence.aif') 15 silence_3 = os.path.join(DATA_DIR, '8k-1ch-1s-silence.aif') 16 silence_4 = os.path.join(DATA_DIR, '8k-1ch-3.5s-silence.aif') 17 silence_5 = os.path.join(DATA_DIR, '8k-4ch-1s-silence.aif') 18 19 has_tags = os.path.join(DATA_DIR, 'with-id3.aif') 20 no_tags = os.path.join(DATA_DIR, '8k-1ch-1s-silence.aif') 21 22 def setUp(self): 23 self.filename_1 = get_temp_copy(self.has_tags) 24 self.filename_2 = get_temp_copy(self.no_tags) 25 26 self.aiff_tmp_id3 = AIFF(self.filename_1) 27 self.aiff_tmp_no_id3 = AIFF(self.filename_2) 28 29 self.aiff_1 = AIFF(self.silence_1) 30 self.aiff_2 = AIFF(self.silence_2) 31 self.aiff_3 = AIFF(self.silence_3) 32 self.aiff_4 = AIFF(self.silence_4) 33 self.aiff_5 = AIFF(self.silence_5) 34 35 def test_channels(self): 36 self.failUnlessEqual(self.aiff_1.info.channels, 1) 37 self.failUnlessEqual(self.aiff_2.info.channels, 2) 38 self.failUnlessEqual(self.aiff_3.info.channels, 1) 39 self.failUnlessEqual(self.aiff_4.info.channels, 1) 40 self.failUnlessEqual(self.aiff_5.info.channels, 4) 41 42 def test_length(self): 43 self.failUnlessEqual(self.aiff_1.info.length, 2) 44 self.failUnlessEqual(self.aiff_2.info.length, 0.1) 45 self.failUnlessEqual(self.aiff_3.info.length, 1) 46 self.failUnlessEqual(self.aiff_4.info.length, 3.5) 47 self.failUnlessEqual(self.aiff_5.info.length, 1) 48 49 def test_bitrate(self): 50 self.failUnlessEqual(self.aiff_1.info.bitrate, 176400) 51 self.failUnlessEqual(self.aiff_2.info.bitrate, 1536000) 52 self.failUnlessEqual(self.aiff_3.info.bitrate, 128000) 53 self.failUnlessEqual(self.aiff_4.info.bitrate, 128000) 54 self.failUnlessEqual(self.aiff_5.info.bitrate, 512000) 55 56 def test_sample_rate(self): 57 self.failUnlessEqual(self.aiff_1.info.sample_rate, 11025) 58 self.failUnlessEqual(self.aiff_2.info.sample_rate, 48000) 59 self.failUnlessEqual(self.aiff_3.info.sample_rate, 8000) 60 self.failUnlessEqual(self.aiff_4.info.sample_rate, 8000) 61 self.failUnlessEqual(self.aiff_5.info.sample_rate, 8000) 62 63 def test_sample_size(self): 64 self.failUnlessEqual(self.aiff_1.info.sample_size, 16) 65 self.failUnlessEqual(self.aiff_2.info.sample_size, 16) 66 self.failUnlessEqual(self.aiff_3.info.sample_size, 16) 67 self.failUnlessEqual(self.aiff_4.info.sample_size, 16) 68 self.failUnlessEqual(self.aiff_5.info.sample_size, 16) 69 70 def test_notaiff(self): 71 self.failUnlessRaises( 72 AIFFError, AIFF, os.path.join(DATA_DIR, 'empty.ofr')) 73 74 def test_pprint(self): 75 self.failUnless(self.aiff_1.pprint()) 76 self.failUnless(self.aiff_tmp_id3.pprint()) 77 78 def test_delete(self): 79 self.aiff_tmp_id3.delete() 80 self.failIf(self.aiff_tmp_id3.tags) 81 self.failUnless(AIFF(self.filename_1).tags is None) 82 83 def test_module_delete(self): 84 delete(self.filename_1) 85 self.failUnless(AIFF(self.filename_1).tags is None) 86 87 def test_module_double_delete(self): 88 delete(self.filename_1) 89 delete(self.filename_1) 90 91 def test_pprint_no_tags(self): 92 self.aiff_tmp_id3.tags = None 93 self.failUnless(self.aiff_tmp_id3.pprint()) 94 95 def test_save_no_tags(self): 96 self.aiff_tmp_id3.tags = None 97 self.aiff_tmp_id3.save() 98 self.assertTrue(self.aiff_tmp_id3.tags is None) 99 100 def test_add_tags_already_there(self): 101 self.failUnless(self.aiff_tmp_id3.tags) 102 self.failUnlessRaises(Exception, self.aiff_tmp_id3.add_tags) 103 104 def test_mime(self): 105 self.failUnless("audio/aiff" in self.aiff_1.mime) 106 self.failUnless("audio/x-aiff" in self.aiff_1.mime) 107 108 def test_loaded_tags(self): 109 self.failUnless(self.aiff_tmp_id3["TIT2"] == "AIFF title") 110 111 def test_roundtrip(self): 112 self.failUnlessEqual(self.aiff_tmp_id3["TIT2"], ["AIFF title"]) 113 self.aiff_tmp_id3.save() 114 new = AIFF(self.aiff_tmp_id3.filename) 115 self.failUnlessEqual(new["TIT2"], ["AIFF title"]) 116 117 def test_save_tags(self): 118 from mutagen.id3 import TIT1 119 tags = self.aiff_tmp_id3.tags 120 tags.add(TIT1(encoding=3, text="foobar")) 121 tags.save() 122 123 new = AIFF(self.aiff_tmp_id3.filename) 124 self.failUnlessEqual(new["TIT1"], ["foobar"]) 125 126 def test_save_with_ID3_chunk(self): 127 from mutagen.id3 import TIT1 128 self.aiff_tmp_id3["TIT1"] = TIT1(encoding=3, text="foobar") 129 self.aiff_tmp_id3.save() 130 self.failUnless(AIFF(self.filename_1)["TIT1"] == "foobar") 131 self.failUnless(self.aiff_tmp_id3["TIT2"] == "AIFF title") 132 133 def test_save_without_ID3_chunk(self): 134 from mutagen.id3 import TIT1 135 self.aiff_tmp_no_id3["TIT1"] = TIT1(encoding=3, text="foobar") 136 self.aiff_tmp_no_id3.save() 137 self.failUnless(AIFF(self.filename_2)["TIT1"] == "foobar") 138 139 def test_corrupt_tag(self): 140 with open(self.filename_1, "r+b") as h: 141 chunk = IFFFile(h)[u'ID3'] 142 h.seek(chunk.data_offset) 143 h.seek(4, 1) 144 h.write(b"\xff\xff") 145 self.assertRaises(AIFFError, AIFF, self.filename_1) 146 147 def test_padding(self): 148 AIFF(self.filename_1).save() 149 self.assertEqual(AIFF(self.filename_1).tags._padding, 1002) 150 AIFF(self.filename_1).save() 151 self.assertEqual(AIFF(self.filename_1).tags._padding, 1002) 152 153 tags = AIFF(self.filename_1) 154 tags.save(padding=lambda x: 1) 155 self.assertEqual(AIFF(self.filename_1).tags._padding, 1) 156 157 tags = AIFF(self.filename_1) 158 tags.save(padding=lambda x: 100) 159 self.assertEqual(AIFF(self.filename_1).tags._padding, 100) 160 161 tags = AIFF(self.filename_1) 162 self.assertRaises(AIFFError, tags.save, padding=lambda x: -1) 163 164 def tearDown(self): 165 os.unlink(self.filename_1) 166 os.unlink(self.filename_2) 167 168 169class TAIFFInfo(TestCase): 170 171 def test_empty(self): 172 fileobj = cBytesIO(b"") 173 self.failUnlessRaises(AIFFError, AIFFInfo, fileobj) 174 175 176class TIFFFile(TestCase): 177 has_tags = os.path.join(DATA_DIR, 'with-id3.aif') 178 no_tags = os.path.join(DATA_DIR, '8k-1ch-1s-silence.aif') 179 180 def setUp(self): 181 self.file_1 = open(self.has_tags, 'rb') 182 self.iff_1 = IFFFile(self.file_1) 183 self.file_2 = open(self.no_tags, 'rb') 184 self.iff_2 = IFFFile(self.file_2) 185 186 self.tmp_1_name = get_temp_copy(self.has_tags) 187 self.file_1_tmp = open(self.tmp_1_name, 'rb+') 188 self.iff_1_tmp = IFFFile(self.file_1_tmp) 189 190 self.tmp_2_name = get_temp_copy(self.no_tags) 191 self.file_2_tmp = open(self.tmp_2_name, 'rb+') 192 self.iff_2_tmp = IFFFile(self.file_2_tmp) 193 194 def tearDown(self): 195 self.file_1.close() 196 self.file_2.close() 197 self.file_1_tmp.close() 198 self.file_2_tmp.close() 199 os.unlink(self.tmp_1_name) 200 os.unlink(self.tmp_2_name) 201 202 def test_has_chunks(self): 203 self.failUnless(u'FORM' in self.iff_1) 204 self.failUnless(u'COMM' in self.iff_1) 205 self.failUnless(u'SSND' in self.iff_1) 206 self.failUnless(u'ID3' in self.iff_1) 207 208 self.failUnless(u'FORM' in self.iff_2) 209 self.failUnless(u'COMM' in self.iff_2) 210 self.failUnless(u'SSND' in self.iff_2) 211 212 def test_is_chunks(self): 213 self.failUnless(isinstance(self.iff_1[u'FORM'], IFFChunk)) 214 self.failUnless(isinstance(self.iff_1[u'COMM'], IFFChunk)) 215 self.failUnless(isinstance(self.iff_1[u'SSND'], IFFChunk)) 216 self.failUnless(isinstance(self.iff_1[u'ID3'], IFFChunk)) 217 218 def test_chunk_size(self): 219 self.failUnlessEqual(self.iff_1[u'FORM'].size, 17096) 220 self.failUnlessEqual(self.iff_2[u'FORM'].size, 16054) 221 222 def test_chunk_data_size(self): 223 self.failUnlessEqual(self.iff_1[u'FORM'].data_size, 17088) 224 self.failUnlessEqual(self.iff_2[u'FORM'].data_size, 16046) 225 226 def test_FORM_chunk_resize(self): 227 self.iff_1_tmp[u'FORM'].resize(17000) 228 self.failUnlessEqual( 229 IFFFile(self.file_1_tmp)[u'FORM'].data_size, 17000) 230 self.iff_2_tmp[u'FORM'].resize(0) 231 self.failUnlessEqual(IFFFile(self.file_2_tmp)[u'FORM'].data_size, 0) 232 233 def test_child_chunk_resize(self): 234 self.iff_1_tmp[u'ID3'].resize(128) 235 236 id3 = self.iff_1_tmp[u'ID3'] 237 id3.write(b"\xff" * 128) 238 self.assertEqual(id3.read(), b"\xff" * 128) 239 240 self.failUnlessEqual(IFFFile(self.file_1_tmp)[u'ID3'].data_size, 128) 241 self.failUnlessEqual( 242 IFFFile(self.file_1_tmp)[u'FORM'].data_size, 16182) 243 244 def test_chunk_delete(self): 245 del self.iff_1_tmp[u'ID3'] 246 self.failIf(u'ID3' in self.iff_1_tmp) 247 self.failIf(u'ID3' in IFFFile(self.file_1_tmp)) 248 self.failUnlessEqual(IFFFile(self.file_1_tmp)[u'FORM'].size, 16054) 249 del self.iff_2_tmp[u'SSND'] 250 self.failIf(u'SSND' in self.iff_2_tmp) 251 self.failIf(u'SSND' in IFFFile(self.file_2_tmp)) 252 self.failUnlessEqual(IFFFile(self.file_2_tmp)[u'FORM'].size, 38) 253 254 def test_insert_chunk(self): 255 self.iff_2_tmp.insert_chunk(u'ID3') 256 257 new_iff = IFFFile(self.file_2_tmp) 258 self.failUnless(u'ID3' in new_iff) 259 self.failUnless(isinstance(new_iff[u'ID3'], IFFChunk)) 260 self.failUnlessEqual(new_iff[u'FORM'].size, 16062) 261 self.failUnlessEqual(new_iff[u'FORM'].data_size, 16054) 262 self.failUnlessEqual(new_iff[u'ID3'].size, 8) 263 self.failUnlessEqual(new_iff[u'ID3'].data_size, 0) 264