Exemplo n.º 1
0
 internal AudioTrackCollection(TableOfContents toc)
 {
     this._toc = toc;
 }
Exemplo n.º 2
0
        private void ApplyCdTextInfo(RedBook.CDTextGroup cdtext, out EBU.LanguageCode[] languages, out AlbumText[] albumText, out TrackText[][] trackText)
        {
            languages = null;
            albumText = null;
            trackText = null;
            var packs = cdtext.Packs;

            if (packs == null || packs.Length == 0)
            {
                Trace.WriteLine("No CD-TEXT information (no packs found).", "CD-TEXT");
                return;
            }
            // Assumption: Valid CD-TEXT blocks must have a SizeInfo entry.
            if (packs.Length < 3 || packs[packs.Length - 1].Type != RedBook.CDTextContentType.SizeInfo)
            {
                Trace.WriteLine("No CD-TEXT information (packs do not end with SizeInfo data).", "CD-TEXT");
                return;
            }
            RedBook.CDTextSizeInfo si;
            {
                var bytes = new byte[36];
                for (var i = 0; i < 3; ++i)
                {
                    Array.Copy(packs[packs.Length - 3 + i].Data, 0, bytes, 12 * i, 12);
                }
                si = Util.MarshalBytesToStructure <RedBook.CDTextSizeInfo>(bytes);
            }
            var blockCount = 8;

            while (blockCount > 0 && si.LastSequenceNumber[blockCount - 1] == 0)
            {
                --blockCount;
            }
            if (blockCount == 0)
            {
                Trace.WriteLine("No CD-TEXT information (size info says there are 0 blocks).", "CD-TEXT");
                return;
            }
            // Now set up the info arrays
            languages = new EBU.LanguageCode[blockCount];
            for (var b = 0; b < blockCount; ++b)
            {
                languages[b] = si.LanguageCode[b];
            }
            albumText = new AlbumText[blockCount];
            {
                var trackCount = (this.LastTrack - this.FirstTrack + 1);
                trackText = new TrackText[trackCount][];
                for (var t = 0; t < trackCount; ++t)
                {
                    trackText[t] = new TrackText[blockCount];
                }
            }
            // Process the blocks
            var p = 0;

            for (var b = 0; b < blockCount; ++b)
            {
                var endpack        = si.LastSequenceNumber[b];
                var titleBytes     = new List <byte>();
                var performerBytes = new List <byte>();
                var lyricistBytes  = new List <byte>();
                var composerBytes  = new List <byte>();
                var arrangerBytes  = new List <byte>();
                var messageBytes   = new List <byte>();
                var identBytes     = new List <byte>();
                var genreBytes     = new List <byte>();
                var codeBytes      = new List <byte>();
                var sizeinfoBytes  = new List <byte>();
                var albumTitle     = false;
                var albumPerformer = false;
                var albumLyricist  = false;
                var albumComposer  = false;
                var albumArranger  = false;
                var albumMessage   = false;
                var albumCode      = false;
                Trace.WriteLine($"Processing CD-TEXT block #{b + 1} (language: {si.LanguageCode[b]})...", "CD-TEXT");
                var dbcs = packs[p].IsUnicode;
                for (; p <= endpack; ++p)
                {
                    var pack = packs[p];
                    if (!pack.IsValid)
                    {
                        Trace.WriteLine($"Ignoring pack #{p + 1} (type: {pack.Type}) because it failed the CRC check.", "CD-TEXT");
                        continue;
                    }
                    if (pack.IsExtension)
                    {
                        Trace.WriteLine($"Ignoring pack #{p + 1} (type: {pack.Type}) because it's flagged as an extension.", "CD-TEXT");
                        continue;
                    }
                    if (pack.IsUnicode != dbcs)
                    {
                        Trace.WriteLine($"Pack #{p + 1} (type: {pack.Type}) has a mismatched DBCS flag.", "CD-TEXT");
                    }
                    switch (pack.Type)
                    {
                    case RedBook.CDTextContentType.Arranger:  arrangerBytes.AddRange(pack.Data); if (pack.ID2 == 0)
                        {
                            albumArranger = true;
                        }
                        break;

                    case RedBook.CDTextContentType.Code:      codeBytes.AddRange(pack.Data); if (pack.ID2 == 0)
                        {
                            albumCode = true;
                        }
                        break;

                    case RedBook.CDTextContentType.Composer:  composerBytes.AddRange(pack.Data); if (pack.ID2 == 0)
                        {
                            albumComposer = true;
                        }
                        break;

                    case RedBook.CDTextContentType.DiscID:    identBytes.AddRange(pack.Data); break;

                    case RedBook.CDTextContentType.Genre:     genreBytes.AddRange(pack.Data); break;

                    case RedBook.CDTextContentType.Lyricist:  lyricistBytes.AddRange(pack.Data); if (pack.ID2 == 0)
                        {
                            albumLyricist = true;
                        }
                        break;

                    case RedBook.CDTextContentType.Messages:  messageBytes.AddRange(pack.Data); if (pack.ID2 == 0)
                        {
                            albumMessage = true;
                        }
                        break;

                    case RedBook.CDTextContentType.Performer: performerBytes.AddRange(pack.Data); if (pack.ID2 == 0)
                        {
                            albumPerformer = true;
                        }
                        break;

                    case RedBook.CDTextContentType.SizeInfo:  sizeinfoBytes.AddRange(pack.Data); break;

                    case RedBook.CDTextContentType.Title:     titleBytes.AddRange(pack.Data); if (pack.ID2 == 0)
                        {
                            albumTitle = true;
                        }
                        break;

                    default:
                        Trace.WriteLine($"Ignoring pack #{p + 1} because it is of ignored or unsupported type '{pack.Type}'.", "CD-TEXT");
                        break;
                    }
                }
                if (sizeinfoBytes.Count == 36)
                {
                    si = Util.MarshalBytesToStructure <RedBook.CDTextSizeInfo>(sizeinfoBytes.ToArray());
                }
                else
                {
                    Trace.WriteLine("Ignoring this block because it does not include size info.", "CD-TEXT");
                    continue;
                }
                // FIXME: Any skipped packs above will cause these checks to fail.
                if (si.PacksWithType80 * 12 != titleBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 80).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType81 * 12 != performerBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 81).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType82 * 12 != lyricistBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 82).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType83 * 12 != composerBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 83).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType84 * 12 != arrangerBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 84).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType85 * 12 != messageBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 85).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType86 * 12 != identBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 86).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType87 * 12 != genreBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 87).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType8E * 12 != codeBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 8E).", "CD-TEXT"); continue;
                }
                if (si.PacksWithType8F * 12 != sizeinfoBytes.Count)
                {
                    Trace.WriteLine("Ignoring this block because it fails validation (pack count, type 8F).", "CD-TEXT"); continue;
                }
                Encoding encoding = null;
                if (dbcs)
                {
                    Trace.WriteLine("This block contains DBCS data; assuming this means UTF-16.", "CD-TEXT");
                    encoding = Encoding.BigEndianUnicode;
                }
                else
                {
                    switch (si.CharacterCode)
                    {
                    case RedBook.CDTextCharacterCode.ISO_646:
                        encoding = Encoding.ASCII;
                        break;

                    case RedBook.CDTextCharacterCode.ISO_8859_1:
                        // FIXME: It's supposed to be a modified form of latin-1, but without the Blue Book, it's unclear what those modifications entail.
                        Trace.WriteLine("Using plain ISO-8859-1 as encoding; some characters may not be correct.", "CD-TEXT");
                        encoding = Encoding.GetEncoding("iso-8859-1");
                        break;

                    case RedBook.CDTextCharacterCode.Korean:
                        Trace.WriteLine("Assuming EUC-KR as Korean encoding.", "CD-TEXT");
                        encoding = Encoding.GetEncoding("euc-kr");
                        break;

                    case RedBook.CDTextCharacterCode.MandarinChinese:
                        Trace.WriteLine("Assuming GB2312 as Mandarin Chinese encoding.", "CD-TEXT");
                        encoding = Encoding.GetEncoding("gb2312");
                        break;

                    case RedBook.CDTextCharacterCode.MusicShiftJis:
                        // FIXME: Without standard RIAJ RS506 it's unclear how this differs from plain Shift-JIS, but some comments online suggest it has a LOT of extra emoji.
                        Trace.WriteLine("Using plain Shift-Jis as encoding; some characters may not be correct.", "CD-TEXT");
                        encoding = Encoding.GetEncoding("iso-2022-jp");
                        break;

                    default:
                        Trace.WriteLine($"Ignoring this block because it specifies an unknown character set ({si.CharacterCode}).", "CD-TEXT");
                        continue;
                    }
                }
                var            latin1           = Encoding.GetEncoding("iso-8859-1");
                BlueBook.Genre?genre            = null;
                string         genreDescription = null;
                if (genreBytes?.Count > 0)
                {
                    var rawgenre = genreBytes.ToArray();
                    var code     = BitConverter.ToInt16(rawgenre, 0);
                    if (BitConverter.IsLittleEndian)
                    {
                        code = IPAddress.NetworkToHostOrder(code);
                    }
                    genre            = (BlueBook.Genre)code;
                    genreDescription = latin1.GetString(rawgenre, 2, rawgenre.Length - 2).TrimEnd('\0');
                    if (genreDescription.Length == 0)
                    {
                        genreDescription = null;
                    }
                }
                string ident = null;
                if (identBytes?.Count > 0)
                {
                    ident = latin1.GetString(identBytes.ToArray()).TrimEnd('\0');
                }
                var tracks     = (si.LastTrack - si.FirstTrack + 1);
                var titles     = TableOfContents.TextValue(RedBook.CDTextContentType.Title, titleBytes, encoding, tracks + (albumTitle     ? 1 : 0));
                var performers = TableOfContents.TextValue(RedBook.CDTextContentType.Performer, performerBytes, encoding, tracks + (albumPerformer ? 1 : 0));
                var lyricists  = TableOfContents.TextValue(RedBook.CDTextContentType.Lyricist, lyricistBytes, encoding, tracks + (albumLyricist  ? 1 : 0));
                var composers  = TableOfContents.TextValue(RedBook.CDTextContentType.Composer, composerBytes, encoding, tracks + (albumComposer  ? 1 : 0));
                var arrangers  = TableOfContents.TextValue(RedBook.CDTextContentType.Arranger, arrangerBytes, encoding, tracks + (albumArranger  ? 1 : 0));
                var messages   = TableOfContents.TextValue(RedBook.CDTextContentType.Messages, messageBytes, encoding, tracks + (albumMessage   ? 1 : 0));
                var codes      = TableOfContents.TextValue(RedBook.CDTextContentType.Code, codeBytes, latin1, tracks + (albumCode      ? 1 : 0));
                {
                    var title     = (albumTitle && titles?.Length > 0) ? titles    [0] : null;
                    var performer = (albumPerformer && performers?.Length > 0) ? performers[0] : null;
                    var lyricist  = (albumLyricist && lyricists?.Length > 0) ? lyricists [0] : null;
                    var composer  = (albumComposer && composers?.Length > 0) ? composers [0] : null;
                    var arranger  = (albumArranger && arrangers?.Length > 0) ? arrangers [0] : null;
                    var message   = (albumMessage && messages?.Length > 0) ? messages  [0] : null;
                    var code      = (albumCode && codes?.Length > 0) ? codes     [0] : null;
                    if (genre.HasValue || title != null || performer != null || lyricist != null || composer != null || arranger != null || message != null || code != null)
                    {
                        albumText[b] = new AlbumText(genre, genreDescription, ident, title, performer, lyricist, composer, arranger, message, code);
                    }
                }
                var delta = si.FirstTrack - this.FirstTrack;
                for (var t = 0; t < tracks; ++t)
                {
                    var idx = 0;
                    if (t + delta < 0)
                    {
                        continue;
                    }
                    if (t + delta >= trackText.Length)
                    {
                        break;
                    }
                    idx = t + (albumTitle     ? 1 : 0); var title = (titles?.Length > idx) ? titles    [idx] : null;
                    idx = t + (albumPerformer ? 1 : 0); var performer = (performers?.Length > idx) ? performers[idx] : null;
                    idx = t + (albumLyricist  ? 1 : 0); var lyricist = (lyricists?.Length > idx) ? lyricists [idx] : null;
                    idx = t + (albumComposer  ? 1 : 0); var composer = (composers?.Length > idx) ? composers [idx] : null;
                    idx = t + (albumArranger  ? 1 : 0); var arranger = (arrangers?.Length > idx) ? arrangers [idx] : null;
                    idx = t + (albumMessage   ? 1 : 0); var message = (messages?.Length > idx) ? messages  [idx] : null;
                    idx = t + (albumCode      ? 1 : 0); var code = (codes?.Length > idx) ? codes     [idx] : null;
                    if (title != null || performer != null || lyricist != null || composer != null || arranger != null || message != null || code != null)
                    {
                        trackText[t][b] = new TrackText(title, performer, lyricist, composer, arranger, message, code);
                    }
                }
            }
        }
Exemplo n.º 3
0
        private static int Main(string[] args)
        {
            try {
                {
                    var sb            = new StringBuilder();
                    var defaultDevice = TableOfContents.DefaultDevice;
                    foreach (var dev in TableOfContents.AvailableDevices)
                    {
                        if (sb.Length > 0)
                        {
                            sb.Append(", ");
                        }
                        sb.Append(dev);
                        if (dev == defaultDevice)
                        {
                            sb.Append(" (default)");
                        }
                    }
                    Console.WriteLine($"Available Devices   : {sb}");
                }
                var features = TableOfContents.AvailableFeatures;
                Console.WriteLine($"Supported Features  : {features}");
                Console.WriteLine();
                string device = null;
                foreach (var arg in args)
                {
                    switch (arg)
                    {
                    case "help":
                    case "-?":
                    case "/?": {
                        Console.WriteLine("Usage: DiscId [OPTIONS] [DEVICE]");
                        Console.WriteLine();
                        Console.WriteLine("Available Options:");
                        Console.WriteLine("  -noisrc     Disable reading of track ISRC values.");
                        Console.WriteLine("  -nomcn      Disable reading of the media catalog number.");
                        Console.WriteLine("  -notext     Disable reading of CD-TEXT info.");
                        Console.WriteLine("  -help, -?   Show this list of options.");
                        return(0);
                    }

                    case "-noisrc":
                    case "/noisrc": features &= ~DiscReadFeature.TrackIsrc;          break;

                    case "-nomcn":
                    case "/nomcn":  features &= ~DiscReadFeature.MediaCatalogNumber; break;

                    case "-notext":
                    case "/notext": features &= ~DiscReadFeature.CdText;             break;

                    default:
                        if (device != null)
                        {
                            throw new ArgumentException("Too many command line arguments given.");
                        }
                        device = arg;
                        break;
                    }
                }
                var toc = TableOfContents.ReadDisc(device, features);
                if (toc == null)
                {
                    Console.WriteLine("No table of contents available.");
                }
                else
                {
                    Console.WriteLine($"CD Device Used      : {toc.DeviceName}");
                    Console.WriteLine($"Features Requested  : {features}");
                    Console.WriteLine();
                    if ((features & DiscReadFeature.MediaCatalogNumber) != 0)
                    {
                        Console.WriteLine($"Media Catalog Number: {toc.MediaCatalogNumber ?? "* not set *"}");
                    }
                    Console.WriteLine($"MusicBrainz Disc ID : {toc.DiscId}");
                    Console.WriteLine($"FreeDB Disc ID      : {toc.FreeDbId}");
                    Console.WriteLine($"Submission URL      : {toc.SubmissionUrl}");
                    Console.WriteLine();
                    var languages = toc.TextLanguages;
                    if (languages?.Count > 0)
                    {
                        var text = toc.TextInfo;
                        if (text?.Count > 0)
                        {
                            Console.WriteLine("CD-TEXT Information:");
                            var idx = 0;
                            foreach (var l in languages)
                            {
                                Console.WriteLine($"- Language: {l}");
                                var ti = text[idx++];
                                if (ti.Genre.HasValue)
                                {
                                    if (ti.GenreDescription != null)
                                    {
                                        Console.WriteLine($"  - Genre           : {ti.Genre.Value} ({ti.GenreDescription})");
                                    }
                                    else
                                    {
                                        Console.WriteLine($"  - Genre           : {ti.Genre.Value}");
                                    }
                                }
                                if (ti.Identification != null)
                                {
                                    Console.WriteLine($"  - Identification  : {ti.Identification}");
                                }
                                if (ti.ProductCode != null)
                                {
                                    Console.WriteLine($"  - UPC/EAN         : {ti.ProductCode}");
                                }
                                if (ti.Title != null)
                                {
                                    Console.WriteLine($"  - Title           : {ti.Title}");
                                }
                                if (ti.Performer != null)
                                {
                                    Console.WriteLine($"  - Performer       : {ti.Performer}");
                                }
                                if (ti.Lyricist != null)
                                {
                                    Console.WriteLine($"  - Lyricist        : {ti.Lyricist}");
                                }
                                if (ti.Composer != null)
                                {
                                    Console.WriteLine($"  - Composer        : {ti.Composer}");
                                }
                                if (ti.Arranger != null)
                                {
                                    Console.WriteLine($"  - Arranger        : {ti.Arranger}");
                                }
                                if (ti.Message != null)
                                {
                                    Console.WriteLine($"  - Message         : {ti.Message}");
                                }
                            }
                            Console.WriteLine();
                        }
                    }
                    Console.WriteLine("Tracks:");
                    { // Check for a "hidden" pre-gap track
                        var t = toc.Tracks[toc.FirstTrack];
                        if (t.StartTime > Program.TwoSeconds)
                        {
                            Console.WriteLine($" --- Offset: {150,6} ({Program.TwoSeconds,-16}) Length: {t.Offset - 150,6} ({t.StartTime.Subtract(Program.TwoSeconds),-16})");
                        }
                    }
                    foreach (var t in toc.Tracks)
                    {
                        Console.Write($" {t.Number,2}. Offset: {t.Offset,6} ({t.StartTime,-16}) Length: {t.Length,6} ({t.Duration,-16})");
                        if ((features & DiscReadFeature.TrackIsrc) != 0)
                        {
                            Console.Write($" ISRC: {t.Isrc ?? "* not set *"}");
                        }
                        Console.WriteLine();
                        if (languages?.Count > 0)
                        {
                            var text = t.TextInfo;
                            if (text?.Count > 0)
                            {
                                Console.WriteLine("     CD-TEXT Information:");
                                var idx = 0;
                                foreach (var l in languages)
                                {
                                    Console.WriteLine($"     - Language: {l}");
                                    var ti = text[idx++];
                                    if (ti.Title != null)
                                    {
                                        Console.WriteLine($"       - Title     : {ti.Title}");
                                    }
                                    if (ti.Performer != null)
                                    {
                                        Console.WriteLine($"       - Performer : {ti.Performer}");
                                    }
                                    if (ti.Lyricist != null)
                                    {
                                        Console.WriteLine($"       - Lyricist  : {ti.Lyricist}");
                                    }
                                    if (ti.Composer != null)
                                    {
                                        Console.WriteLine($"       - Composer  : {ti.Composer}");
                                    }
                                    if (ti.Arranger != null)
                                    {
                                        Console.WriteLine($"       - Arranger  : {ti.Arranger}");
                                    }
                                    if (ti.Message != null)
                                    {
                                        Console.WriteLine($"       - Message   : {ti.Message}");
                                    }
                                    if (ti.Isrc != null)
                                    {
                                        Console.WriteLine($"       - ISRC      : {ti.Isrc}");
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e) {
                Program.ReportExceptionOnConsole(e);
                return(1);
            }
            return(0);
        }