libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

generate.pike (12838B)


      1 #! /usr/bin/env pike
      2 #pike 7.5
      3 
      4 //
      5 // A small program that generates a ID3v1/ID3v1.1 testsuite.
      6 // Copyright (c) 2003 Martin Nilsson
      7 //
      8 // This code is far from good looking, but the code itself isn't
      9 // really the interesting thing here...
     10 //
     11 
     12 #if !constant(ADT.Struct)
     13 #error This Pike is too old for this application.
     14 #endif
     15 
     16 // 64Kbit/s, 32kHz, ~222.22 frames/s, 288 bytes/frame
     17 constant silence_lead_in = "˙ûXÄ\0\0\0\0\1¤\0\0\0\0\0\0""4\200\0\0\0" +
     18 ("˙"*267) + "˙ûXÄ\205\200\2ā\1¤\0\0\0\0\0\0""4\200\0\0\0" + ("˙"*267);
     19 constant silence_frame = "˙ûXÄ˙\200!`\1¤\0\0\0\0\0\0""4\200\0\0\0" + ("˙"*267);
     20 
     21 // Generate silent MP3 data, either as a returned string or
     22 // outputted on @[f]. The length is either @[s] seconds or
     23 // three frames, in case @[s] is zero or omitted.
     24 void|string generate_silence(void|int s, void|Stdio.File f) {
     25   int frames = (int)(64000/288.0 * s);
     26   frames = max(frames-2, 0);
     27   if(f) {
     28     f->write(silence_lead_in);
     29     for(; frames; frames--)
     30       f->write(silence_frame);
     31     return;
     32   }
     33   return silence_lead_in + silence_frame * frames;
     34 }
     35 
     36 // The structure of an ID3v1 tag.
     37 class ID3_1 {
     38   inherit ADT.Struct;
     39   Item head = Chars(3, "TAG");
     40   Item title = Chars(30, "\0"*30);
     41   Item artist = Chars(30, "\0"*30);
     42   Item album = Chars(30, "\0"*30);
     43   Item year = Chars(4, "2003");
     44   Item comment = Chars(30, "\0"*30);
     45   Item genre = Byte(0);
     46 }
     47 
     48 // The structure of an ID3v1.1 tag.
     49 class ID3_11 {
     50   inherit ADT.Struct;
     51   Item head = Chars(3, "TAG");
     52   Item title = Chars(30, "\0"*30);
     53   Item artist = Chars(30, "\0"*30);
     54   Item album = Chars(30, "\0"*30);
     55   Item year = Chars(4, "2003");
     56   Item comment = Chars(28, "\0"*28);
     57   Item null = Byte(0);
     58   Item track = Byte(0);
     59   Item genre = Byte(0);
     60 }
     61 
     62 // Pads a string with null to @[size] characters. Default is 30.
     63 string pad(string in, void|int size) {
     64   if(!size) size=30;
     65   if(sizeof(in)>size) error("String longer than %d chars.\n", size);
     66   return in+("\0"*(size-sizeof(in)));
     67 }
     68 
     69 // Removes the null padding from a string.
     70 string strip_pad(string in) {
     71   sscanf(reverse(in), "%*[\0]%s", in);
     72   return reverse(in);
     73 }
     74 
     75 // Prints out information about an ID3 tag on stdout.
     76 void show_tag(string data) {
     77   if(sizeof(data)!=128) error("Wrong tag size.\n");
     78 
     79 #define ITEM(X) write("%-7s: %O\n", #X, strip_pad(tag->X))
     80   object tag;
     81   if(data[-3]==0 && data[-2]!=0)
     82     tag = ID3_11(data);
     83   else
     84     tag = ID3_1(data);
     85 
     86   write("%-7s: %s\n", "version", tag->track?"1.1":"1.0");
     87   ITEM(head);
     88   ITEM(title);
     89   ITEM(artist);
     90   ITEM(album);
     91   ITEM(year);
     92   ITEM(comment);
     93   if(tag->track)
     94     write("%-7s: %O\n", "track", tag->track);
     95   string genre;
     96   catch( genre = id3_genres[tag->genre] );
     97   write("%-7s: %O (%s)\n", "genre", tag->genre,
     98 	genre||"unknown");
     99 }
    100 
    101 array(string) id3_genres = ({
    102   "Blues", // 0
    103   "Classic Rock",
    104   "Country",
    105   "Dance",
    106   "Disco",
    107   "Funk",
    108   "Grunge",
    109   "Hip-Hop",
    110   "Jazz",
    111   "Metal",
    112   "New Age",
    113   "Oldies",
    114   "Other",
    115   "Pop",
    116   "R&B",
    117   "Rap",
    118   "Reggae",
    119   "Rock",
    120   "Techno",
    121   "Industrial",
    122   "Alternative",
    123   "Ska",
    124   "Death Metal",
    125   "Pranks",
    126   "Soundtrack",
    127   "Euro-Techno",
    128   "Ambient",
    129   "Trip-Hop",
    130   "Vocal",
    131   "Jazz+Funk",
    132   "Fusion",
    133   "Trance",
    134   "Classical",
    135   "Instrumental",
    136   "Acid",
    137   "House",
    138   "Game",
    139   "Sound Clip",
    140   "Gospel",
    141   "Noise",
    142   "AlternRock",
    143   "Bass",
    144   "Soul",
    145   "Punk",
    146   "Space",
    147   "Meditative",
    148   "Instrumental Pop",
    149   "Instrumental Rock",
    150   "Ethnic",
    151   "Gothic",
    152   "Darkwave",
    153   "Techno-Industrial",
    154   "Electronic",
    155   "Pop-Folk",
    156   "Eurodance",
    157   "Dream",
    158   "Southern Rock",
    159   "Comedy",
    160   "Cult",
    161   "Gangsta",
    162   "Top 40",
    163   "Christian Rap",
    164   "Pop/Funk",
    165   "Jungle",
    166   "Native American",
    167   "Cabaret",
    168   "New Wave",
    169   "Psychadelic",
    170   "Rave",
    171   "Showtunes",
    172   "Trailer",
    173   "Lo-Fi",
    174   "Tribal",
    175   "Acid Punk",
    176   "Acid Jazz",
    177   "Polka",
    178   "Retro",
    179   "Musical",
    180   "Rock & Roll",
    181   "Hard Rock", // 79
    182   "Folk",
    183   "Folk-Rock",
    184   "National Folk",
    185   "Swing",
    186   "Fast Fusion",
    187   "Bebob",
    188   "Latin",
    189   "Revival",
    190   "Celtic",
    191   "Bluegrass",
    192   "Avantgarde",
    193   "Gothic Rock",
    194   "Progressive Rock",
    195   "Psychedelic Rock",
    196   "Symphonic Rock",
    197   "Slow Rock",
    198   "Big Band",
    199   "Chorus",
    200   "Easy Listening",
    201   "Acoustic",
    202   "Humour",
    203   "Speech",
    204   "Chanson",
    205   "Opera",
    206   "Chamber Music",
    207   "Sonata",
    208   "Symphony",
    209   "Booty Bass",
    210   "Primus",
    211   "Porn Groove",
    212   "Satire",
    213   "Slow Jam",
    214   "Club",
    215   "Tango",
    216   "Samba",
    217   "Folklore",
    218   "Ballad",
    219   "Power Ballad",
    220   "Rhythmic Soul",
    221   "Freestyle",
    222   "Duet",
    223   "Punk Rock",
    224   "Drum Solo",
    225   "A capella",
    226   "Euro-House",
    227   "Dance Hall", // 125
    228   "Goa",
    229   "Drum & Bass",
    230   "Club-House",
    231   "Hardcore",
    232   "Terror",
    233   "Indie",
    234   "BritPop",
    235   "Negerpunk",
    236   "Polsk Punk",
    237   "Beat",
    238   "Christian",
    239   "Heavy Metal",
    240   "Black Metal",
    241   "Crossover",
    242   "Contemporary",
    243   "Christian Rock",
    244   "Merengue",
    245   "Salsa",
    246   "Thrash Metal",
    247   "Anime",
    248   "JPop",
    249   "Synthpop",
    250 });
    251 
    252 int global_test_counter;
    253 string path = "id3v1/";
    254 array(string) m3u = ({});
    255 
    256 // Test template
    257 class tt {
    258   string desc;
    259   int complience;
    260 
    261   string fn() {
    262     string c = "";
    263     if(complience==1) c="_W"; // Warning
    264     if(complience>1) c="_F"; // Failure
    265     return sprintf("id3v1_%03d_%s%s.mp3", global_test_counter, sect, c);
    266   }
    267 
    268   string tag() { return ""; };
    269 
    270   void create() {
    271     global_test_counter++;
    272     m3u += ({ fn() });
    273     Stdio.write_file( path + fn(),
    274 		      generate_silence()+tag() );
    275     write("Test case %d\n", global_test_counter);
    276     write("Generated test file %O\n", fn());
    277     werror("Generated test file %O\n", fn());
    278     if(!desc) error("Missing description.\n");
    279     write("%-=70s\n", desc);
    280     if(complience>1) write("Test case should generate a decoding failure.\n");
    281     if(complience==1) write("Test case might generate a decoding warning.\n");
    282     write("Tag structure\n");
    283     show_tag(tag());
    284     write("\n");
    285   }
    286 }
    287 
    288 string sect;
    289 
    290 void tests() {
    291 
    292   sect = "basic";
    293   write("Test cases that tests basic tag capabilities.\n\n");
    294 
    295   class {
    296     inherit tt;
    297     string desc = "An ordinary ID3v1 tag with all fields set to a "
    298     "plauseble value.";
    299     string tag() {
    300       object tag = ID3_1();
    301       tag->title = pad("Title");
    302       tag->artist = pad("Artist");
    303       tag->album = pad("Album");
    304       tag->year = "2003";
    305       tag->genre = 7;
    306       tag->comment = pad("Comment");
    307       return (string)tag;
    308     }
    309   }();
    310 
    311   class {
    312     inherit tt;
    313     string desc = "An ordinary ID3v1.1 tag with all fields set to a "
    314     "plauseble value.";
    315     string tag() {
    316       object tag = ID3_11();
    317       tag->title = pad("Title");
    318       tag->artist = pad("Artist");
    319       tag->album = pad("Album");
    320       tag->year = "2003";
    321       tag->genre = 7;
    322       tag->comment = pad("Comment", 28);
    323       tag->track = 12;
    324       return (string)tag;
    325     }
    326   }();
    327 
    328   class {
    329     inherit tt;
    330     string desc = "An ID3 tag with its header in the wrong case.";
    331     int complience = 2;
    332     string tag() {
    333       object tag = ID3_1();
    334       tag->head = "tag";
    335       return (string)tag;
    336     }
    337   }();
    338 
    339   class {
    340     inherit tt;
    341     string desc = "An ID3 tag with all fields set to shortest legal value.";
    342     string tag() {
    343       object tag = ID3_1();
    344       return (string)tag;
    345     }
    346   }();
    347 
    348   class {
    349     inherit tt;
    350     string desc = "An ID3v1 tag with all fields set to longest value.";
    351     string tag() {
    352       object tag = ID3_1();
    353       tag->title = "a"*29+"A";
    354       tag->artist = "b"*29+"B";
    355       tag->album = "c"*29+"C";
    356       tag->comment = "d"*29+"D";
    357       return (string)tag;
    358     }
    359   }();
    360 
    361   class {
    362     inherit tt;
    363     string desc = "An ID3v1.1 tag with all fields set to longest value.";
    364     string tag() {
    365       object tag = ID3_11();
    366       tag->title = "a"*29+"A";
    367       tag->artist = "b"*29+"B";
    368       tag->album = "c"*29+"C";
    369       tag->comment = "d"*27+"D";
    370       tag->track = 1;
    371       return (string)tag;
    372     }
    373   }();
    374 
    375   class {
    376     inherit tt;
    377     string desc = "An ID3v1 tag with junk after string terminator. "
    378     "The junk should not show up for the user (i.e. only the string "
    379     "12345 should show up).";
    380     int complience = 1;
    381     string tag() {
    382       object tag = ID3_1();
    383       tag->title = "12345" + "\0"*21 + "junk";
    384       tag->artist = "12345" + "\0"*21 + "junk";
    385       tag->album = "12345" + "\0"*21 + "junk";
    386       tag->comment = "12345" + "\0"*21 + "junk";
    387       return (string)tag;
    388     }
    389   }();
    390 
    391   class {
    392     inherit tt;
    393     string desc = "An ID3v1 tag with junk after string terminator. "
    394     "The junk should not show up for the user (i.e. only the string "
    395     "12345 should show up).";
    396     int complience = 1;
    397     string tag() {
    398       object tag = ID3_11();
    399       tag->title = "12345" + "\0"*21 + "junk";
    400       tag->artist = "12345" + "\0"*21 + "junk";
    401       tag->album = "12345" + "\0"*21 + "junk";
    402       tag->comment = "12345" + "\0"*19 + "junk";
    403       tag->track = 1;
    404       return (string)tag;
    405     }
    406   }();
    407 
    408   class {
    409     inherit tt;
    410     string desc = "An ID3 tag with the track number set to max (255).";
    411     string tag() {
    412       object tag = ID3_11();
    413       tag->track = 255;
    414       return (string)tag;
    415     }
    416   }();
    417 
    418   sect = "year";
    419   write("\nDifferent tests that tries to break the year parser.\n\n");
    420 
    421   class Year {
    422     inherit tt;
    423     string year;
    424     string tag() {
    425       object tag = ID3_1();
    426       tag->year = year;
    427       return (string)tag;
    428     }
    429   };
    430 
    431   class {
    432     inherit Year;
    433     string desc = "An ID3 tag with the year set to 0000.\n";
    434     string year = "0000";
    435   }();
    436 
    437   class {
    438     inherit Year;
    439     string desc = "An ID3 tag with the year set to 9999.\n";
    440     string year = "9999";
    441   }();
    442 
    443   class {
    444     inherit Year;
    445     string desc = "An ID3 tag with the year set to \"   3\".\n";
    446     int complience = 2;
    447     string year = "   3";
    448   }();
    449 
    450   class {
    451     inherit Year;
    452     string desc = "An ID3 tag with the year set to \"112\\0\".\n";
    453     int complience = 2;
    454     string year = "112\0";
    455   }();
    456 
    457   class {
    458     inherit Year;
    459     string desc = "An ID3 tag with the year set to NULL.\n";
    460     int complience = 2;
    461     string year = "\0\0\0\0";
    462   }();
    463 
    464   sect = "genre";
    465   write("\nTests that tests the genre capabilities.\n\n");
    466   foreach(id3_genres; int i; string name) {
    467     class {
    468       inherit tt;
    469       string name;
    470       int genre;
    471       string desc = "An ID3 tag with genre set to ";
    472       string tag() {
    473 	object tag = ID3_1();
    474 	tag->title = pad(name);
    475 	tag->genre = genre;
    476 	return (string)tag;
    477       }
    478       void create(string _name, int _genre) {
    479 	name = _name;
    480 	genre = _genre;
    481 	desc += name+".";
    482 	if(genre>79) {
    483 	  complience = 1;
    484 	  desc += " Only the first 80 genres are defined in the original ID3.";
    485 	}
    486 	::create();
    487       }
    488     }(name, i);
    489   }
    490 
    491   for(int i=sizeof(id3_genres); i<256; i++)
    492     class {
    493       inherit tt;
    494       string desc = "An ID3 tag with genre set to ";
    495       int complience = 2;
    496       int g;
    497       string tag() {
    498 	object tag = ID3_1();
    499 	tag->title = pad("Unknown/"+g);
    500 	tag->genre = g;
    501 	return (string)tag;
    502       }
    503       void create(int _g) {
    504 	g = _g;
    505 	desc += g + ".";
    506 	::create();
    507       }
    508     }(i);
    509 
    510   sect = "extra";
    511   write("\nTests to test charset decoding and similar optional "
    512 	"capabilities.\n\n");
    513 
    514   class {
    515     inherit tt;
    516     string desc = "Title with 8-bit iso-8859-1 characters (would be written "
    517     "as r&auml;ksm&ouml;rg&aring;s in HTML).";
    518     string tag() {
    519       object tag = ID3_1();
    520       tag->title = pad("räksmörgås");
    521       tag->artist = pad("räksmörgås");
    522       tag->album = pad("räksmörgås");
    523       tag->comment = pad("räksmörgås");
    524       return (string)tag;
    525     }
    526   }();
    527 
    528   class {
    529     inherit tt;
    530     string desc = "Title with utf-8-encoded 8-bit string (would be written "
    531     "as r&auml;ksm&ouml;rg&aring;s in HTML).";
    532     string tag() {
    533       object tag = ID3_1();
    534       tag->title = pad(string_to_utf8("räksmörgås"));
    535       tag->artist = pad(string_to_utf8("räksmörgås"));
    536       tag->album = pad(string_to_utf8("räksmörgås"));
    537       tag->comment = pad(string_to_utf8("räksmörgås"));
    538       return (string)tag;
    539     }
    540   }();
    541 
    542   class {
    543     inherit tt;
    544     string desc = "Comment field with http://-style URL.";
    545     string tag() {
    546       object tag = ID3_1();
    547       tag->comment = pad("http://www.id3.org/");
    548       return (string)tag;
    549     }
    550   }();
    551 
    552   class {
    553     inherit tt;
    554     string desc = "Comment field with unprefixed URL.";
    555     string tag() {
    556       object tag = ID3_1();
    557       tag->comment = pad("www.id3.org/");
    558       return (string)tag;
    559     }
    560   }();
    561 }
    562 
    563 #define TEE(X...) do { werror(X); write(X); } while(0)
    564 
    565 void main(int num, array args) {
    566 
    567   // FIXME:
    568   // --output-dir    Where to put the files
    569   // --only-correct  Don't create W or F files.
    570   // --length        How many seconds of MP3 silence
    571 
    572   TEE("ID3v1/ID3v1.1 test suite\n");
    573   TEE("Copyright (c) 2003 Martin Nilsson\n");
    574   TEE("Output generated %s\n", Calendar.now()->format_mtime());
    575   TEE("Generated with %s\n\n", version());
    576   tests();
    577   Stdio.write_file(path+"tags.m3u", m3u*"\n");
    578 }