I maintain a small .NET library FlacLibSharp which some people seem to find useful.

But an issue was reported that seemed to indicate part of the API was not user friendly.


The problem lies with the Vorbis Comment implementation. In a simple form, a Vorbis Comment is a key-value pair. For example: "ARTIST" = "Aaron", where "ARTIST" is the key, and "Aaron" is the value.

However, it's possible to add different values for the same key. I only realised this later, so I had to add it to the existing implementation.

In order to cope with it, I introduced a VorbisCommentValues class:

VorbisCommentValues values = flacFile.VorbisComment["ARTIST"];

And additionally, I introduced a "Value" property, that only returns the first value:

string value = flacFile.VorbisComment["ARTIST"].Value;

The problem

The problem arises when writing vorbis data to a flac file:

file.VorbisComment["replay_gain-track_gain"] = 
    new VorbisCommentValues(new string[] { "-2.50 db", "-3.50 db" });
file.VorbisComment["replay_gain-track_gain"].Value = "-2.50 db";

Above, the first lines set the two values for the replay gain tag. The last line sets a single value.

Strangely, the API is missing a way to delete a Vorbis Comment.

Adding and updating tags works in an identical way. The following line will either add the replay gain tag or update its value.

file.VorbisComment["replay_gain-track_gain"].Value = "-2.50 db";

This isn't always intuitive. So it would make sense to include an Add, Update and Remove on the VorbisComment class itself.

The Solution

Forgetting about the technical details of what is underneath the API. A possible solution could look like this:

Removing a complete Vorbis Comment:


Removing a single Vorbis Comment value:

file.VorbisComment["tagname"].Remove("value"); // By value
file.VorbisComment["tagname"].Remove(0); // By index

These two are already available, because VorbisCommentValues is a List.

Adding a vorbis comment could look like this:

file.VorbisComment.Add("tagname", "value");
// Same as this line (if tagname doesn't exist yet):
file.VorbisComment["tagname"].Value = "value";
// Multiple values:
file.VorbisComment.Add("tagname", new string[] { "First Value", "Second Value" });
file.VorbisComment["tagname"] = new VorbisCommentValues(new string[] { "First Value", "Second Value" });

A question arises in this scenario: should the "Add" method throw an error if a comment with that key already exists?

Or should the "Add" append or replace new values?

I think calling Add implies a desire to add a vorbis comment. Not append values nor update existing values. So to me it would make sense that Add will throw an exception if it already has an item with that key.

This means we'd better support Update as well:

file.VorbisComment.Update("tagname", "value");
file.VorbisComment.Update("tagname", new string[] { "First Value", "Second Value" });
// Same as the following
file.VorbisComment["tagname"].Value = "value";
file.VorbisComment["tagname"] = new VorbisCommentValues(new string[] { "First Value", "Second Value" });

This updates the entire list of values for the given tagname.

For convenience, we could also add AddOrUpdate method.

We don't really need to add anything to manipulate the list of values. For example to Append or Insert values, to update specific values, ...

The VorbisCommentValues object is a list, so one can use all the available functions already. For example:

file.VorbisComment["tagname"].Add("New value");
file.VorbisComment["tagname"].Remove("New value");
file.VorbisComment["tagname"][1] = "New value.";

Summary of new API functions on VorbisComment

.Remove(string tagname);
.Add(string tagname, string value);
.Add(string tagname, IEnumerable<string> values);
.Update(string tagname, string value);
.Update(string tagname, IEnumerable<string> values);
.AddOrUpdate(string tagname, string value);
.AddOrUpdate(string tagname, IEnumerable<string> values);


The requirement to call .Value or create a new instance of VorbisCommentValues was a bit strange. These functions will make it more convenient to add tags with a single value or list of values.

It also adds the possibility to remove a tag, which was not yet possible.

These things are all backward compatible new features, so according to SemVer, which I loosely follow, it should increment the MINOR version.