Merge git://github.com/inforichland/factor-id3 into inforichland
						commit
						b93b827fb8
					
				| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					IN: id3
 | 
				
			||||||
 | 
					USING: help.markup help.syntax sequences kernel ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HELP: id3-parse-mp3-file
 | 
				
			||||||
 | 
					{ $values { "path" "a path string" } { "object/f" "either a tuple consisting of the data from an MP3 file, or an f indicating this file has no ID3 information." }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ARTICLE: "id3" "ID3 tags"
 | 
				
			||||||
 | 
					{ $emphasis "id3" } " tags are textual data that is used to describe the information (title, artist, etc.) in an .MP3 file"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ABOUT: "id3"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,182 @@
 | 
				
			||||||
 | 
					! Copyright (C) 2009 Tim Wawrzynczak
 | 
				
			||||||
 | 
					! See http://factorcode.org/license.txt for BSD license.
 | 
				
			||||||
 | 
					USING: tools.test id3 ;
 | 
				
			||||||
 | 
					IN: id3.tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[ T{ mp3v2-file
 | 
				
			||||||
 | 
					     { header  T{ header f t 0 502 } }
 | 
				
			||||||
 | 
					     { frames
 | 
				
			||||||
 | 
					       {
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "COMM" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 19 }
 | 
				
			||||||
 | 
					              { data "eng, AG# 08E1C12E" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "TIT2" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 15 }
 | 
				
			||||||
 | 
					              { data "Stormy Weather" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "TRCK" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 3 }
 | 
				
			||||||
 | 
					              { data "32" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "TCON" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 5 }
 | 
				
			||||||
 | 
					              { data "(96)" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "TALB" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 28 }
 | 
				
			||||||
 | 
					              { data "Night and Day Frank Sinatra" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "PRIV" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 39 }
 | 
				
			||||||
 | 
					              { data "WM/MediaClassPrimaryID<49>}`<60>#<23><>K<EFBFBD>H<EFBFBD>*(D" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "PRIV" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 41 }
 | 
				
			||||||
 | 
					              { data "WM/MediaClassSecondaryID" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					           T{ frame
 | 
				
			||||||
 | 
					              { frame-id "TPE1" }
 | 
				
			||||||
 | 
					              { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					              { size 14 }
 | 
				
			||||||
 | 
					              { data "Frank Sinatra" }
 | 
				
			||||||
 | 
					           }
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					] [ "resource:extra/id3/tests/blah3.mp3" id3-parse-mp3-file ] unit-test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[
 | 
				
			||||||
 | 
					    T{ mp3v2-file
 | 
				
			||||||
 | 
					    { header
 | 
				
			||||||
 | 
					        T{ header { version t } { flags 0 } { size 1405 } }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    { frames
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TIT2" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 22 }
 | 
				
			||||||
 | 
					                { data "Anthem of the Trinity" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TPE1" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 12 }
 | 
				
			||||||
 | 
					                { data "Terry Riley" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TALB" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 11 }
 | 
				
			||||||
 | 
					                { data "Shri Camel" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TCON" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 10 }
 | 
				
			||||||
 | 
					                { data "Classical" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "UFID" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 23 }
 | 
				
			||||||
 | 
					                { data "http://musicbrainz.org" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TXXX" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 23 }
 | 
				
			||||||
 | 
					                { data "MusicBrainz Artist Id" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TXXX" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 22 }
 | 
				
			||||||
 | 
					                { data "musicbrainz_artistid" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TRCK" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 2 }
 | 
				
			||||||
 | 
					                { data "1" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TXXX" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 22 }
 | 
				
			||||||
 | 
					                { data "MusicBrainz Album Id" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TXXX" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 21 }
 | 
				
			||||||
 | 
					                { data "musicbrainz_albumid" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TXXX" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 29 }
 | 
				
			||||||
 | 
					                { data "MusicBrainz Album Artist Id" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TXXX" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 27 }
 | 
				
			||||||
 | 
					                { data "musicbrainz_albumartistid" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TPOS" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 2 }
 | 
				
			||||||
 | 
					                { data "1" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TSOP" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 1 }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            T{ frame
 | 
				
			||||||
 | 
					                { frame-id "TMED" }
 | 
				
			||||||
 | 
					                { flags B{ 0 0 } }
 | 
				
			||||||
 | 
					                { size 4 }
 | 
				
			||||||
 | 
					                { data "DIG" }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					] [ "resource:extra/id3/tests/blah2.mp3" id3-parse-mp3-file ] unit-test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[    
 | 
				
			||||||
 | 
					  T{ mp3v1-file
 | 
				
			||||||
 | 
					     { title
 | 
				
			||||||
 | 
					       "BLAH\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					     { artist
 | 
				
			||||||
 | 
					       "ARTIST\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					     { album
 | 
				
			||||||
 | 
					       "ALBUM\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					     { year "2009" }
 | 
				
			||||||
 | 
					     { comment
 | 
				
			||||||
 | 
					       "COMMENT\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					     { genre 89 }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					] [ "resource:extra/id3/tests/blah.mp3" id3-parse-mp3-file ] unit-test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,154 @@
 | 
				
			||||||
 | 
					! Copyright (C) 2009 Tim Wawrzynczak
 | 
				
			||||||
 | 
					! See http://factorcode.org/license.txt for BSD license.
 | 
				
			||||||
 | 
					USING: sequences io io.encodings.binary io.files io.pathnames strings kernel math io.mmap io.mmap.uchar accessors syntax combinators math.ranges unicode.categories byte-arrays prettyprint io.encodings.string io.encodings.ascii ;
 | 
				
			||||||
 | 
					IN: id3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! tuples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TUPLE: header version flags size ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TUPLE: frame frame-id flags size data ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TUPLE: mp3v2-file header frames ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TUPLE: mp3v1-file title artist album year comment genre ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: <mp3v1-file> ( -- object ) mp3v1-file new ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: <mp3v2-file> ( header frames -- object ) mp3v2-file boa ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: <header> ( -- object ) header new ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: <frame> ( -- object ) frame new ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<PRIVATE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! utility words
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: id3v2? ( mmap -- ? )
 | 
				
			||||||
 | 
					    "ID3" head? ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: id3v1? ( mmap -- ? )
 | 
				
			||||||
 | 
					    128 tail-slice* "TAG" head? ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: >28bitword ( seq -- int )
 | 
				
			||||||
 | 
					    0 [ swap 7 shift bitor ] reduce ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: filter-text-data ( data -- filtered )
 | 
				
			||||||
 | 
					    [ printable? ] filter ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! frame details stuff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: valid-frame-id? ( id -- ? )
 | 
				
			||||||
 | 
					    [ [ digit? ] [ LETTER? ] bi or ] all? ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-frame-id ( mmap -- id )
 | 
				
			||||||
 | 
					    4 head-slice ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-frame-size ( mmap -- size )
 | 
				
			||||||
 | 
					    [ 4 8 ] dip subseq ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-frame-flags ( mmap -- flags )
 | 
				
			||||||
 | 
					    [ 8 10 ] dip subseq ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-frame-data ( frame mmap -- frame data )
 | 
				
			||||||
 | 
					    [ 10 over size>> 10 + ] dip <slice> filter-text-data ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! read whole frames
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: (read-frame) ( mmap -- frame )
 | 
				
			||||||
 | 
					    [ <frame> ] dip
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [ read-frame-id    ascii decode >>frame-id ]
 | 
				
			||||||
 | 
					        [ read-frame-flags >byte-array  >>flags ]
 | 
				
			||||||
 | 
					        [ read-frame-size  >28bitword   >>size ]
 | 
				
			||||||
 | 
					        [ read-frame-data  ascii decode >>data ]
 | 
				
			||||||
 | 
					    } cleave ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-frame ( mmap -- frame/f )
 | 
				
			||||||
 | 
					    dup read-frame-id valid-frame-id? [ (read-frame) ] [ drop f ] if ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: remove-frame ( mmap frame -- mmap )
 | 
				
			||||||
 | 
					    size>> 10 + tail-slice ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-frames ( mmap -- frames )
 | 
				
			||||||
 | 
					    [ dup read-frame dup ]
 | 
				
			||||||
 | 
					    [ [ remove-frame ] keep ]
 | 
				
			||||||
 | 
					    [ drop ] produce nip ;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					! header stuff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-header-supported-version? ( mmap -- ? )
 | 
				
			||||||
 | 
					    3 tail-slice [ { 4 } head? ] [ { 3 } head? ] bi or ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-header-flags ( mmap -- flags )
 | 
				
			||||||
 | 
					    5 swap nth ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-header-size ( mmap -- size )
 | 
				
			||||||
 | 
					    [ 6 10 ] dip <slice> >28bitword ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-v2-header ( mmap -- id3header )
 | 
				
			||||||
 | 
					    [ <header> ] dip
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [ read-header-supported-version?  >>version ]
 | 
				
			||||||
 | 
					        [ read-header-flags >>flags ]
 | 
				
			||||||
 | 
					        [ read-header-size >>size ]
 | 
				
			||||||
 | 
					    } cleave ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: drop-header ( mmap -- seq1 seq2 )
 | 
				
			||||||
 | 
					    dup 10 tail-slice swap ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-v2-tag-data ( seq -- mp3v2-file )
 | 
				
			||||||
 | 
					    drop-header read-v2-header swap read-frames <mp3v2-file> ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! v1 information
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: skip-to-v1-data ( seq -- seq )
 | 
				
			||||||
 | 
					    125 tail-slice* ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-title ( seq -- title )
 | 
				
			||||||
 | 
					    30 head-slice ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-artist ( seq -- title )
 | 
				
			||||||
 | 
					    [ 30 60 ] dip subseq ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-album ( seq -- album )
 | 
				
			||||||
 | 
					    [ 60 90 ] dip subseq ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-year ( seq -- year )
 | 
				
			||||||
 | 
					    [ 90 94 ] dip subseq ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-comment ( seq -- comment )
 | 
				
			||||||
 | 
					    [ 94 124 ] dip subseq ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-genre ( seq -- genre )
 | 
				
			||||||
 | 
					    [ 124 ] dip nth ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: (read-v1-tag-data) ( seq -- mp3-file )
 | 
				
			||||||
 | 
					    [ <mp3v1-file> ] dip
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        [ read-title   ascii decode  >>title ]
 | 
				
			||||||
 | 
					        [ read-artist  ascii decode  >>artist ]
 | 
				
			||||||
 | 
					        [ read-album   ascii decode  >>album ]
 | 
				
			||||||
 | 
					        [ read-year    ascii decode  >>year ]
 | 
				
			||||||
 | 
					        [ read-comment ascii decode  >>comment ]
 | 
				
			||||||
 | 
					        [ read-genre   >fixnum       >>genre ]
 | 
				
			||||||
 | 
					    } cleave ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: read-v1-tag-data ( seq -- mp3-file )
 | 
				
			||||||
 | 
					    skip-to-v1-data (read-v1-tag-data) ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PRIVATE>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! main stuff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					: id3-parse-mp3-file ( path -- object )
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            { [ dup id3v2? ] [ read-v2-tag-data ] } ! ( ? -- mp3v2-file )
 | 
				
			||||||
 | 
					            { [ dup id3v1? ] [ read-v1-tag-data ] } ! ( ? -- mp3v1-file )
 | 
				
			||||||
 | 
					            [ drop f ] ! ( mmap -- f )
 | 
				
			||||||
 | 
					        } cond
 | 
				
			||||||
 | 
					    ] with-mapped-uchar-file ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					! end
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
		Reference in New Issue