### Add Chapters to Track Source: https://github.com/zeugma440/atldotnet/wiki/Changelog-(discontinued) Example of adding chapters to a track where each chapter automatically starts at the end of the previous one. ```csharp theTrack.Chapters.Add(new ChapterInfo(0, "Prologue: Mei")); theTrack.Chapters.Add(new ChapterInfo(790 * 1000, "Chapter One: Bobbie")); theTrack.Chapters.Add(new ChapterInfo(2214 * 1000, "Chapter Three: Prax")); ``` -------------------------------- ### Read and write sample metadata on a WAV file Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Shows how to handle sample-specific metadata including manufacturer info, MIDI unity notes, and loop points. Includes logic for iterating through loop points and modifying or removing them. ```csharp using ATL; using ATL.AudioData; // Load audio file information into memory Track theTrack = new Track(audioFilePath); // Display general data int manufacturer = 0; if (theTrack.AdditionalFields.ContainsKey("sample.manufacturer")) manufacturer = int.Parse(theTrack.AdditionalFields["sample.manufacturer"]); int midiUnityNote = 0; if (theTrack.AdditionalFields.ContainsKey("sample.MIDIUnityNote")) midiUnityNote = int.Parse(theTrack.AdditionalFields["sample.MIDIUnityNote"]); System.Console.WriteLine("Manufacturer : " + manufacturer); System.Console.WriteLine("MIDI Unity note : " + midiUnityNote); // Display loop points data int nbLoopPoints = 0; if (theTrack.AdditionalFields.ContainsKey("sample.NumSampleLoops")) nbLoopPoints = int.Parse(theTrack.AdditionalFields["sample.NumSampleLoops"]); for (int i = 0; i < nbLoopPoints; i++) { int type = 0; if (theTrack.AdditionalFields.ContainsKey("sample.SampleLoop[" + i + "].Type")) type = int.Parse(theTrack.AdditionalFields["sample.SampleLoop[" + i + "].Type"]); int start = 0; if (theTrack.AdditionalFields.ContainsKey("sample.SampleLoop[" + i + "].Start")) start = int.Parse(theTrack.AdditionalFields["sample.SampleLoop[" + i + "].Start"]); int end = 0; if (theTrack.AdditionalFields.ContainsKey("sample.SampleLoop[" + i + "].End")) end = int.Parse(theTrack.AdditionalFields["sample.SampleLoop[" + i + "].End"]); System.Console.WriteLine("Sample[" + i + "] : Type " + type + " : " + start + "->" + end); } // Modify data theTrack.AdditionalFields["sample.MIDIUnityNote"] = "61"; theTrack.AdditionalFields["sample.SampleLoop[0].Start"] = "1000"; theTrack.AdditionalFields["sample.SampleLoop[0].End"] = "2000"; // Add new sample loop theTrack.AdditionalFields["sample.SampleLoop[1].Start"] = "3000"; theTrack.AdditionalFields["sample.SampleLoop[1].End"] = "4000"; // Remove sample loop (all sub-fields after [x] should be removed) theTrack.AdditionalFields.Remove("sample.SampleLoop[2].Start"); theTrack.AdditionalFields.Remove("sample.SampleLoop[2].End"); theTrack.Save(); ``` -------------------------------- ### New PictureInfo.fromBinaryData static constructor (v3.00) Source: https://github.com/zeugma440/atldotnet/wiki/Changelog-(discontinued) Shows the recommended way to create a PictureInfo in v3.00 and later, using the static fromBinaryData method to load binary data and auto-detect the image format. ```csharp byte[] data = System.IO.File.ReadAllBytes("E:/temp/_Images/pic1.gif"); PictureInfo newPicture = PictureInfo.fromBinaryData(data, PictureInfo.PIC_TYPE.CD); ``` -------------------------------- ### Read and write BEXT, INFO, and iXML metadata on a WAV file Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Loads a WAV file, reads specific metadata fields (bext.originator, info.IENG, ixml.SCENE), modifies them, and saves the changes. Uses TryGetValue to safely access the fields. ```csharp using ATL; using ATL.AudioData; // Load audio file information into memory Track theTrack = new Track(audioFilePath); // Display BEXT, LIST INFO and iXML data string originator = "", engineer = "", scene = ""; if (theTrack.AdditionalFields.ContainsKey(if (theTrack.AdditionalFields.TryGetValue("bext.originator", out var field)) originator = field; if (theTrack.AdditionalFields.TryGetValue("info.IENG", out var field2)) engineer = field2; if (theTrack.AdditionalFields.TryGetValue("ixml.SCENE", out var field3)) scene = field3; System.Console.WriteLine("Originator : " + originator); System.Console.WriteLine("Engineer : " + engineer); System.Console.WriteLine("Scene : " + scene); // Modify data theTrack.AdditionalFields["bext.originator"] = "Dave Johnson"; theTrack.AdditionalFields["info.IENG"] = "John Jackman"; theTrack.AdditionalFields["ixml.SCENE"] = "42"; theTrack.Save(); ``` -------------------------------- ### Read and write XMP metadata on a WAV file Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Demonstrates how to load a WAV file, access specific XMP fields via AdditionalFields, and save modifications back to the file. ```csharp using ATL; using ATL.AudioData; // Load audio file information into memory Track theTrack = new Track(audioFilePath); // Display XMP data string photoshopSource = "", rating = "", composer = ""; if (theTrack.AdditionalFields.TryGetValue("xmp.rdf:RDF.rdf:Description.photoshop:Source", out var field)) photoshopSource = field; if (theTrack.AdditionalFields.TryGetValue("xmp.rdf:RDF.rdf:Description.xmp:Rating", out var field2)) rating = field2; if (theTrack.AdditionalFields.TryGetValue("xmp.rdf:RDF.rdf:Description.xmpDM:composer", out var field3)) composer = field3; System.Console.WriteLine("Source : " + photoshopSource); System.Console.WriteLine("Rating : " + rating); System.Console.WriteLine("Composer : " + composer); // Modify data theTrack.AdditionalFields["xmp.rdf:RDF.rdf:Description.photoshop:Source"] = "Company A"; theTrack.AdditionalFields["xmp.rdf:RDF.rdf:Description.xmp:Rating"] = "5"; theTrack.AdditionalFields["xmp.rdf:RDF.rdf:Description.xmpDM:composer"] = "Dave Johnson"; theTrack.Save(); ``` -------------------------------- ### Reading an audio file (audio data & metadata) Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Demonstrates how to load an audio file into a Track object and access standard metadata fields, audio properties, and custom fields. ```csharp using ATL; using ATL.AudioData; // Adapt this to whatever your file path needs to be string fileName = "C:/temp/MP3/test.mp3"; // Load audio file information into memory Track theTrack = new Track(fileName); // That's it ! Now try and display standard fields System.Console.WriteLine("Title : " + theTrack.Title); System.Console.WriteLine("Artist : " + theTrack.Artist); System.Console.WriteLine("Album : " + theTrack.Album); System.Console.WriteLine("Recording year : " + theTrack.Year); System.Console.WriteLine("Track number : " + theTrack.TrackNumber); System.Console.WriteLine("Disc number : " + theTrack.DiscNumber); System.Console.WriteLine("Genre : " + theTrack.Genre); System.Console.WriteLine("Comment : " + theTrack.Comment); // ATL also supports fields with multiple values var artists = theTrack.Artist.Split(ATL.Settings.DisplayValueSeparator); foreach (var artist in artists) System.Console.WriteLine("Artist : " + artist); System.Console.WriteLine("Duration (s) : " + theTrack.Duration); System.Console.WriteLine("Bitrate (KBps) : " + theTrack.Bitrate); System.Console.WriteLine("Number of channels : " + theTrack.ChannelsArrangement.NbChannels); System.Console.WriteLine("Channels arrangement : " + theTrack.ChannelsArrangement.Description); System.Console.WriteLine("Has variable bitrate audio : " + (theTrack.IsVBR ? "yes" : "no")); System.Console.WriteLine("Has lossless audio : " + (AudioDataIOFactory.CF_LOSSLESS == theTrack.CodecFamily ? "yes" : "no")); // Display custom fields (e.g. TXXX values in ID3v2, or any other custom tag) foreach (System.Collections.Generic.KeyValuePair field in theTrack.AdditionalFields) { System.Console.WriteLine("Custom field " + field.Key + " : value = " + field.Value); } ``` -------------------------------- ### Old PictureInfo constructor with ImageFormat (v3.00 breaking change) Source: https://github.com/zeugma440/atldotnet/wiki/Changelog-(discontinued) Shows the obsolete way to create a PictureInfo before v3.00, using the constructor that takes an ImageFormat argument. This approach is replaced by PictureInfo.fromBinaryData. ```csharp PictureInfo newPicture = new PictureInfo(Commons.ImageFormat.Gif, PictureInfo.PIC_TYPE.CD); newPicture.PictureData = System.IO.File.ReadAllBytes("E:/temp/_Images/pic1.gif"); ``` -------------------------------- ### Initialize and Modify Audio Metadata Source: https://github.com/zeugma440/atldotnet/blob/main/README.md Demonstrates how to initialize a Track object from a file path or stream, access metadata properties, update fields including custom ones, and save changes. ```csharp using ATL.AudioData; // Initialize with a file path Track theTrack = new Track(audioFilePath); // Works the same way on any supported format (MP3, FLAC, WMA, SPC...) System.Console.WriteLine("Title : " + theTrack.Title); System.Console.WriteLine("Duration (ms) : " + theTrack.DurationMs); theTrack.Composer = "Oscar Wilde (アイドル)"; // Support for "exotic" charsets theTrack.AdditionalFields["customField"] = "fancyValue"; // Support for custom fields theTrack.Save(); // Can alternatively be initialized with a Stream theTrack = new Track(stream); ``` -------------------------------- ### Reading playlist contents Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Demonstrates how to read playlist contents by either retrieving file paths or ready-to-use Track instances. ```csharp using ATL; using ATL.Playlist; IPlaylistIO theReader = PlaylistIOFactory.GetInstance().GetPlaylistIO(playlistPath); // Option A : Get file paths foreach (string s in theReader.FilePaths) { System.Console.WriteLine(s); } // Option B : Get ready-to-use tracks (instances of ATL.Track) foreach (Track t in theReader.Tracks) { System.Console.WriteLine(t.Title); } ``` -------------------------------- ### Writing playlist contents Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Shows how to write playlist contents by assigning either a list of file paths or a list of Track instances, followed by a call to Save(). ```csharp using ATL; using ATL.Playlist; IPlaylistIO pls = PlaylistIOFactory.GetInstance().GetPlaylistIO(playlistFilePath); // Option A : Writing file paths IList pathsToWrite = new List(); pathsToWrite.Add(filePath1); pathsToWrite.Add(filePath2); pls.FilePaths = pathsToWrite; pls.Save(); // Option B : Writing tracks (instances of ATL.Track) IList tracksToWrite = new List(); tracksToWrite.Add(track1); tracksToWrite.Add(track2); pls.Tracks = tracksToWrite; pls.Save(); ``` -------------------------------- ### Listing supported file formats Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Generates a semicolon-separated string of supported file extensions by iterating through readable formats provided by the PlaylistIOFactory. ```csharp using ATL; using ATL.AudioData; using ATL.Playlist; System.Text.StringBuilder filter = new System.Text.StringBuilder(""); foreach (Format f in PlaylistIOFactory.GetInstance().getFormats()) { if (f.Readable) { foreach (string extension in f) { filter.Append(extension).Append(";"); } } } // Removes the last separator filter.Remove(filter.Length - 1, 1); ``` -------------------------------- ### Monitoring write progress with a direct call Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Use an Action delegate to track progress during a synchronous write operation. ```csharp private void displayProgress(float progress) { Console.WriteLine(progress * 100 + "%"); // You may call Application.DoEvents() here if you're updating UI elements } private void performWrite() { Action progress = new Action(displayProgress); Track t = new Track(path); t.AdditionalFields.Add(new KeyValuePair("test","aaa")); t.Save(progress); } ``` -------------------------------- ### Write ID3v2.3 tags using Settings.ID3v2_tagSubVersion Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Demonstrates how to force the ID3v2 tag sub-version to 3 (ID3v2.3) before saving a track, overriding the default ID3v2.4. ```csharp using ATL; using ATL.AudioData; // Adapt this to whatever your file path needs to be string fileName = "E:/temp/MP3/test.mp3"; // Load audio file information into memory Track theTrack = new Track(fileName); // Force sub-version to be ID3v2.3 Settings.ID3v2_tagSubVersion = 3; // Save modifications to storage theTrack.Save(); ``` -------------------------------- ### Receiving ATL logs with ILogDevice Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Implement the ILogDevice interface to capture log messages from the ATL library. The constructor registers the device with the LogDelegator. ```csharp using ATL; using ATL.Logging; public class LoggingTest : ILogDevice { Log theLog = new Log(); System.Collections.Generic.IList messages = new System.Collections.Generic.IList(); public LoggingTest() { LogDelegator.SetLog(ref theLog); theLog.Register(this); } public void TestSyncMessage() { messages.Clear(); LogDelegator.GetLocateDelegate()("file name"); LogDelegator.GetLogDelegate()(Log.LV_DEBUG, "test message 1"); LogDelegator.GetLogDelegate()(Log.LV_WARNING, "test message 2"); System.Console.WriteLine(messages[0].Message); } public void DoLog(Log.LogItem anItem) { messages.Add(anItem); } } ``` -------------------------------- ### Updating metadata (embedded pictures) Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Illustrates how to remove existing embedded pictures and add new ones from binary data, followed by saving the changes. ```csharp using ATL; using ATL.AudioData; // Adapt this to whatever your file path needs to be string fileName = "E:/temp/MP3/test.mp3"; // Load audio file information into memory Track theTrack = new Track(fileName); // Delete first embedded picture (let's say it exists) theTrack.EmbeddedPictures.RemoveAt(0); // Add 'CD' embedded picture PictureInfo newPicture = PictureInfo.fromBinaryData(System.IO.File.ReadAllBytes(imagePath), PictureInfo.PIC_TYPE.CD); theTrack.EmbeddedPictures.Add(newPicture); // Save modifications to storage theTrack.Save(); ``` -------------------------------- ### Write chapters to an audio tag Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Shows how to add chapter information, including titles, subtitles, URLs, and pictures, to a track. It is recommended to set ID3v2_tagSubVersion to 3 when targeting ID3v2 chapters for better compatibility. ```csharp using ATL; using ATL.AudioData; // Note : if you target ID3v2 chapters, it is highly advised to use Settings.ID3v2_tagSubVersion = 3 // as most readers only support ID3v2.3 chapters Track theFile = new Track(audioFilePath); readonly string imagePath1 = "someFolder/pic1.jpeg"; readonly string imagePath2 = "someFolder/pic2.jpeg"; theFile.Chapters = new System.Collections.Generic.List(); ChapterInfo ch = new ChapterInfo(); ch.StartTime = 0; ch.Title = "title1"; ch.UniqueID = ""; // Unique ID is specific to ID3v2 chapters ch.Subtitle = "subtitle1"; // Substitle is specific to ID3v2 chapters ch.Url = new ChapterInfo.UrlInfo("somewhere", "https://some.whe.re"); // Chapter URL is specific to ID3v2 chapters ch.Picture = PictureInfo.fromBinaryData(System.IO.File.ReadAllBytes(imagePath1)); // Pictures are supported by ID3v2 and MP4/M4A theFile.Chapters.Add(ch); ch = new ChapterInfo(1230, "title2"); // Faster that way :-) ch.UniqueID = "002"; ch.Subtitle = "subtitle2"; ch.Url = new ChapterInfo.UrlInfo("anywhere", "https://any.whe.re"); ch.Picture = PictureInfo.fromBinaryData(System.IO.File.ReadAllBytes(imagePath2)); theFile.Chapters.Add(ch); // Persists the chapters theFile.Save(); // Reads the file again from sratch theFile = new Track(audioFilePath); IList pics = theFile.EmbeddedPictures; // Hack to load chapter pictures // Display chapters foreach (ChapterInfo chap in theFile.Chapters) { System.Console.WriteLine(chap.Title + "(" + chap.StartTime + ")"); } ``` -------------------------------- ### Write and read lyrics in a track Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Shows how to add unsynchronized and synchronized lyrics to a Track, save the file, and read the lyrics back. Synchronized lyrics can be added programmatically with timestamps or parsed from LRC/SRT format strings. ```csharp using ATL; using ATL.AudioData; using Commons; Track theFile = new Track(audioFilePath); theFile.Lyrics = new List(); LyricsInfo info = new LyricsInfo(); theFile.Lyrics.Add(info); info.LanguageCode = "eng"; info.Description = "song"; // Option A : Unsynchronized lyrics info.UnsynchronizedLyrics = "I'm the one\r\n中を"; // Option B : Synchronized lyrics (programmatically) info.ContentType = LyricsInfo.LyricsType.LYRICS; info.SynchronizedLyrics.Add(new LyricsInfo.LyricsPhrase(12000, "I'm the one")); // 12s timestamp info.SynchronizedLyrics.Add(new LyricsInfo.LyricsPhrase("00:00:45", "中を")); // 45s timestamp // Option C : Synchronized lyrics (from LRC or SRT formats) info.UnsynchronizedLyrics = "[00:28.581]<00:28.581>I'm <00:28.981>wishing <00:29.797>on <00:30.190>a <00:30.629>star<00:31.575>\r\n[00:31.877]<00:31.877>And <00:32.245>trying <00:33.109>to <00:33.525>believe<00:34.845>\r\n"; // Persists the chapters theFile.Save(); // Reads the file again theFile = new Track(audioFilePath); // Display lyrics foreach (LyricsInfo lyrics in theFile.Lyrics) { System.Console.WriteLine(lyrics.UnsynchronizedLyrics); foreach (LyricsInfo.LyricsPhrase phrase in lyrics.SynchronizedLyrics) { System.Console.WriteLine("[" + Utils.EncodeTimecode_ms(phrase.TimestampStart) + "] " + phrase.Text); } } ``` -------------------------------- ### Updating metadata (textual fields) Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Shows how to modify standard and non-standard metadata fields on a track and save the changes back to the file. ```csharp using ATL; using ATL.AudioData; // Adapt this to whatever your file path needs to be string fileName = "C:/temp/MP3/test.mp3"; // Load audio file information into memory Track theTrack = new Track(fileName); // Modify metadata // Set standard values theTrack.Artist = "Hey ho"; theTrack.Composer = "Oscar Wilde"; // Set multiple standard values theTrack.Genre = "Country" + ATL.Settings.DisplayValueSeparator + "Disco"; // Set non-standard values theTrack.AdditionalFields["customField"] = "fancyValue"; // Save modifications to storage theTrack.Save(); ``` -------------------------------- ### Reading embedded pictures in a tag Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Demonstrates how to load an audio file and extract its embedded pictures into a list of PictureInfo objects, which can then be converted to .NET Image objects. ```csharp using ATL; using ATL.AudioData; // Adapt this to whatever your file path needs to be string fileName = "E:/temp/MP3/test.mp3"; // Load audio file information into memory Track theTrack = new Track(fileName); // Get picture list System.Collections.Generic.IList embeddedPictures = theTrack.EmbeddedPictures; // Transform them into .NET Image, if needed foreach (PictureInfo pic in embeddedPictures) { System.Drawing.Image image = System.Drawing.Image.FromStream(new System.IO.MemoryStream(pic.PictureData)); } ``` -------------------------------- ### Monitoring write progress with an asynchronous call Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Use IProgress with the SaveAsync method to track progress during an asynchronous write operation. ```csharp private void displayProgress(float progress) { Console.WriteLine(progress * 100 + "%"); } private async void performWrite() { IProgress progress = new Progress(displayProgress); Track t = new Track(path); t.AdditionalFields.Add(new KeyValuePair("test","aaa")); await t.SaveAsync(progress); } ``` -------------------------------- ### Reading CUE Sheet contents Source: https://github.com/zeugma440/atldotnet/wiki/3.-Usage-_-Code-snippets Retrieves metadata and track information from a CUE sheet using the CatalogDataReaderFactory. ```csharp using ATL; using ATL.CatalogDataReaders; ICatalogDataReader theReader = CatalogDataReaderFactory.GetInstance().GetCatalogDataReader(cuesheetPath); System.Console.WriteLine(theReader.Artist); System.Console.WriteLine(theReader.Title); System.Console.WriteLine(theReader.Comments); foreach (Track t in theReader.Tracks) { System.Console.WriteLine(">" + t.Title); } ```