Skip to content

3. Usage _ Code snippets

Zeugma440 edited this page Mar 2, 2024 · 42 revisions

Library usage / Code snippets

For much more use cases and working sample code, feel free to browse the ATL.test project (home of the many unit tests that ensure each commit still works as designed)

Feel free to drop a line through the Issues form if you need to add a specific use case here


Reading an audio file (audio data & metadata)

Reading embedded pictures in a tag

Updating metadata (textual fields)

Updating metadata (embedded pictures)

Writing chapters in a tag

Writing and reading lyrics

Reading and writing BEXT, INFO and iXML metadata on a WAV file

Reading and writing XMP metadata on a WAV file

Reading and writing sample metadata on a WAV file

Reading playlist contents

Writing playlist contents

Reading CUE Sheet contents

Listing supported file formats

Receiving ATL logs in your app

Monitoring progress of writing operations


Reading an audio file (audio data & metadata)

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 classic 'supported' 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);

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<string, string> field in theTrack.AdditionalFields)
{
	System.Console.WriteLine("Custom field " + field.Key + " : value = " + field.Value);
}

Reading embedded pictures in a tag

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<PictureInfo> 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));
}

Updating metadata (textual fields)

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
theTrack.Artist = "Hey ho";
theTrack.Composer = "Oscar Wilde";
theTrack.AdditionalFields["customField"] = "fancyValue";

// Save modifications on the disc
theTrack.Save();

Updating metadata (embedded pictures)

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 on the disc
theTrack.Save();

Writing chapters in a tag

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>();

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<PictureInfo> pics = theFile.EmbeddedPictures; // Hack to load chapter pictures

// Display chapters
foreach (ChapterInfo chap in theFile.Chapters)
{
	System.Console.WriteLine(chap.Title + "(" + chap.StartTime + ")");
}

Writing and reading lyrics

using ATL;
using ATL.AudioData;
using Commons;

Track theFile = new Track(audioFilePath);

theFile.Lyrics = new LyricsInfo();
theFile.Lyrics.LanguageCode = "eng";
theFile.Lyrics.Description = "song";

// Option A : Unsynchronized lyrics
theFile.Lyrics.UnsynchronizedLyrics = "I'm the one\r\n中を";

// Option B : Synchronized lyrics
theFile.Lyrics.ContentType = LyricsInfo.LyricsType.LYRICS;
theFile.Lyrics.SynchronizedLyrics.Add(new LyricsInfo.LyricsPhrase(12000, "I'm the one")); // 12s timestamp
theFile.Lyrics.SynchronizedLyrics.Add(new LyricsInfo.LyricsPhrase("00:00:45", "中を"));   // 45s timestamp

// Persists the chapters
theFile.Save();

// Reads the file again
theFile = new Track(audioFilePath);

// Display lyrics
System.Console.WriteLine(theFile.Lyrics.UnsynchronizedLyrics);
foreach (LyricsInfo.LyricsPhrase phrase in theFile.Lyrics.SynchronizedLyrics)
{
	System.Console.WriteLine("[" + Utils.EncodeTimecode_ms(phrase.TimestampMs) + "] " + phrase.Text);
}

Reading and writing BEXT, INFO and iXML metadata on a WAV file

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();

Reading and writing XMP metadata on a WAV file

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 and writing sample metadata on a WAV file

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();

Reading playlist contents

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

using ATL;
using ATL.Playlist;

IPlaylistIO pls = PlaylistIOFactory.GetInstance().GetPlaylistIO(playlistFilePath);

// Option A : Writing file paths
IList<string> pathsToWrite = new List<string>();
pathsToWrite.Add(filePath1);
pathsToWrite.Add(filePath2);
pls.FilePaths = pathsToWrite;

// Option B : Writing tracks (instances of ATL.Track)
IList<Track> tracksToWrite = new List<Track>();
tracksToWrite.Add(track1);
tracksToWrite.Add(track2);
pls.Tracks = tracksToWrite;

Reading CUE Sheet contents

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);
}

Listing supported file formats

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);

Receiving ATL logs in your app

using ATL;
using ATL.Logging;

public class LoggingTest : ILogDevice
{
	Log theLog = new Log();
	System.Collections.Generic.IList<Log.LogItem> messages = new System.Collections.Generic.IList<Log.LogItem>();

	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);
	}
}

Monitoring progress of writing operations

See the dedicated page for more information

Direct call

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<float> progress = new Action<float>(displayProgress);
	Track t = new Track(path);

	t.AdditionalFields.Add(new KeyValuePair<string, string>("test","aaa"));

	t.Save(progress);
}

Asynchronous call

private void displayProgress(float progress)
{
	Console.WriteLine(progress * 100 + "%");
}

private async void performWrite()
{
	IProgress<float> progress = new Progress<float>(displayProgress);
	Track t = new Track(path);

	t.AdditionalFields.Add(new KeyValuePair<string, string>("test","aaa"));

	await t.SaveAsync(progress);
}