Author Topic: Ogg Comments and WMA tag editing in C#  (Read 8427 times)

Adam

  • Guest
Ogg Comments and WMA tag editing in C#
« on: 6 Jan '06 - 21:54 »
Does anyone have any libraries or insight into I could edit Ogg comments or WMA tags in C#?

I'm using the Tag extension and found a nice open source ID3v2 tag library, but can't seem to find much for the other supported formats.

Thanks :)
Adam

radio42

  • Posts: 4681
Re: Ogg Comments and WMA tag editing in C#
« Reply #1 on: 7 Jan '06 - 14:04 »
If you would just like to edit (read/write) WMA and MP3 (ID3v2) Tags you might want to use the standard "WMFSDKFunctions" - they are available from Microsoft and there are also C# wrappers available.
If wanted I might post some wrappers here...

But if you also need to edit other formats, like OGG - I would suggest using "AudioGenie" from http://www.audiogenie.de - which supports almost any Tags for almost all audio formats...

Adam

  • Guest
Re: Ogg Comments and WMA tag editing in C#
« Reply #2 on: 7 Jan '06 - 19:57 »
Thanks Radio, that is very helpful.

I would very much like to see your WMFSDKFunctions wrappers, thanks.

If I can't find another way to edit OGG tags I'll give audiogenie a go. I have a brilliant ID3v2 editor in place, I just need something for WMA and OGG. Are there any other formats popular enough for the PC that I should bother making a tag editor for?

One question you may know the answer to: would the WMFSDKFunctions require a certain version of WMP be installed?

Thanks
Adam

radio42

  • Posts: 4681
Re: Ogg Comments and WMA tag editing in C#
« Reply #3 on: 8 Jan '06 - 11:25 »
Hi Adam,

the WMFSDKFunctions.cs comes directly from the Microsoft Windows Media SDK.
It can be found here and either WMSDK v10 or v9 are fine to work with:
http://www.microsoft.com/windows/windowsmedia/mp10/sdk.aspx
Or directly here (if you don't want to install the whole SDK):
http://rampage.theficus.com/development/MP3%20Magick%20Source/MP3%20Magick/MP3%20Magick%20Common/WMFSDKFunctions.cs

The rest is straight forward, e.g. in C#:
Code: [Select]
MetadataEditor = null;
IWMHeaderInfo3 HeaderInfo3;
WMFSDKFunctions.WMCreateEditor( out MetadataEditor );
MetadataEditor.Open( "your filename goes here" ) ;
HeaderInfo3 = ( IWMHeaderInfo3 )MetadataEditor;
...
// try reading plain audio file tags .wma, .mp3, .wmv
ushort langIdx = 0;
ushort[] nullIdx = null;
ushort wAttributeCount = 0;
HeaderInfo3.GetAttributeIndices( 0, null, ref langIdx, nullIdx, ref wAttributeCount);
ushort[] attribIndices = new ushort[wAttributeCount];
HeaderInfo3.GetAttributeIndices( 0, null, ref langIdx, attribIndices, ref wAttributeCount);
if (attribIndices != null && attribIndices.Length > 0)
{
foreach (ushort wAttribIndex in attribIndices)
{
WMT_ATTR_DATATYPE   wAttribType;
string              pwszAttribName = null;
object              pbAttribValue = null;
ushort              wAttribNameLen = 0;
uint                dwAttribValueLen = 0;
try
{
HeaderInfo3.GetAttributeByIndexEx( 0, wAttribIndex, pwszAttribName, ref wAttribNameLen, out wAttribType, out langIdx, IntPtr.Zero, ref dwAttribValueLen);
pwszAttribName = new String( ( char )0, wAttribNameLen );
switch (wAttribType)
{
case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
pbAttribValue = (uint)0;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
pbAttribValue = Guid.NewGuid();
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
pbAttribValue = (ulong)0;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
pbAttribValue = (ushort)0;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
pbAttribValue = new byte[dwAttribValueLen];
break;
default:
throw new InvalidOperationException(string.Format("Not supported data type: {0}", wAttribType.ToString()));
}
GCHandle h = GCHandle.Alloc(pbAttribValue, GCHandleType.Pinned);
try
{
IntPtr ptr = h.AddrOfPinnedObject();
HeaderInfo3.GetAttributeByIndexEx( 0, wAttribIndex, pwszAttribName, ref wAttribNameLen, out wAttribType, out langIdx, ptr, ref dwAttribValueLen );
switch (wAttribType)
{
case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
pbAttribValue = Marshal.PtrToStringUni(ptr);
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
pbAttribValue = ((uint)pbAttribValue != 0);
break;
}
// HERE YOU HAVE A TAG!!!
}
finally
{
h.Free();
}
}
catch
{
// some tag index can not be gathered - just ignor this
}
}
}

MetadataEditor.Close();

When saving attributes (Tags) you need to differentiate between NEW Tags to write or modifying existing Tags:
The following methods should help you out:

Code: [Select]
//------------------------------------------------------------------------------
// Name: SetAttrib()
// Desc: Adds or Modifis an attribute
//------------------------------------------------------------------------------
private bool WMSetAttrib( string pAttribName, WMT_ATTR_DATATYPE pAttribType, object pAttribValue )
{
bool ok = true;
try
{
IWMHeaderInfo3 HeaderInfo3 = ( IWMHeaderInfo3 )MetadataEditor;

if (!pAttribName.EndsWith("\0"))
pAttribName += (char)0;

// first try to get Attribut-Index...
ushort langIdx = 0;
ushort[] nullIdx = null;
ushort attribCount = 0;
HeaderInfo3.GetAttributeIndices( 0, pAttribName, ref langIdx, nullIdx, ref attribCount);
ushort[] attribIndices = new ushort[attribCount];
HeaderInfo3.GetAttributeIndices( 0, pAttribName, ref langIdx, attribIndices, ref attribCount);
if (attribCount == 0)
{
// new attibute...use AddAttribute...
if ( WMAddAttrib( pAttribName, pAttribType, pAttribValue, langIdx) == 0)
ok = false;
}
else if (attribIndices != null && attribIndices.Length > 0)
{
// existing attibute...use ModifyAttribute...we always modify the first occurence
ushort modifIdx = attribIndices[0];
// write the attribute
ok = WMModifyAttrib( modifIdx, pAttribType, pAttribValue, langIdx);
}
}
catch { ok = false; }
return ok;
}

Code: [Select]
//------------------------------------------------------------------------------
// Name: AddAttrib()
// Desc: Adds an new attribute with the specifed language index.
//------------------------------------------------------------------------------
private uint WMAddAttrib( string pAttribName, WMT_ATTR_DATATYPE pAttribType, object pAttribValue, ushort pLangIndex )
{
if (!pAttribName.EndsWith("\0"))
pAttribName += (char)0;

object obj;
uint size;
ushort attribIndex = 0;
switch (pAttribType)
{
case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
byte[] arr = Encoding.Unicode.GetBytes((string)pAttribValue+(char)0);
obj = arr;
size = (ushort)arr.Length;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
obj = (uint)( (bool)pAttribValue ? 1 : 0 );
size = 4;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
obj = (byte[])pAttribValue;
size = (ushort)((byte[])obj).Length;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
obj = (uint)pAttribValue;
size = 4;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
obj = (ulong)pAttribValue;
size = 8;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
obj = (ushort)pAttribValue;
size = 2;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
obj = (Guid)pAttribValue;
size = (ushort)Marshal.SizeOf(typeof(Guid));
break;
default:
throw new ArgumentException("Invalid data type", "pAttribType");         
}
GCHandle h = GCHandle.Alloc(obj, GCHandleType.Pinned);
try
{
IWMHeaderInfo3 HeaderInfo3 = ( IWMHeaderInfo3 )MetadataEditor;
HeaderInfo3.AddAttribute( 0, pAttribName, out attribIndex, pAttribType, pLangIndex, h.AddrOfPinnedObject(), size);
}
catch { attribIndex = 0; }
finally
{
h.Free();
}

return attribIndex;
}

Code: [Select]
//------------------------------------------------------------------------------
// Name: ModifyAttrib()
// Desc: Modifies the value of the specified attribute.
//------------------------------------------------------------------------------
private bool WMModifyAttrib( ushort pAttribIndex, WMT_ATTR_DATATYPE pAttribType, object pAttribValue, ushort pLangIndex )
{
bool ok = true;
object obj;
uint size;
switch (pAttribType)
{
case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
byte[] arr = Encoding.Unicode.GetBytes((string)pAttribValue+(char)0);
obj = arr;
size = (ushort)arr.Length;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
obj = (uint)( (bool)pAttribValue ? 1 : 0 );
size = 4;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
obj = (byte[])pAttribValue;
size = (ushort)((byte[])obj).Length;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
obj = (uint)pAttribValue;
size = 4;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
obj = (ulong)pAttribValue;
size = 8;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
obj = (ushort)pAttribValue;
size = 2;
break;
case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
obj = (Guid)pAttribValue;
size = (ushort)Marshal.SizeOf(typeof(Guid));
break;
default:
throw new ArgumentException("Invalid data type", "pAttribType");         
}
GCHandle h = GCHandle.Alloc(obj, GCHandleType.Pinned);
try
{
IWMHeaderInfo3 HeaderInfo3 = ( IWMHeaderInfo3 )MetadataEditor;
HeaderInfo3.ModifyAttribute( 0, pAttribIndex, pAttribType, pLangIndex, h.AddrOfPinnedObject(), size);
}
catch { ok = false; }
finally
{
h.Free();
}

return ok;
}

public enum WMT_ATTR_DATATYPE : ushort
{
  WMT_TYPE_DWORD   = 0,
  WMT_TYPE_STRING  = 1,
  WMT_TYPE_BINARY  = 2,
  WMT_TYPE_BOOL    = 3,
  WMT_TYPE_QWORD   = 4,
  WMT_TYPE_WORD    = 5,
  WMT_TYPE_GUID    = 6,
}

Adam

  • Guest
Re: Ogg Comments and WMA tag editing in C#
« Reply #4 on: 12 Jan '06 - 17:42 »
Thanks again radio, you've been immensely helpful.

I couldn't find a good way to edit Ogg tags in C# so I just went ahead and purchased the AudioGenie, which actually turned out to be ideal.

It has universal reading/writing interfaces for Artist, Album, Title, etc that work with nearly every format. Very nice.

Thanks,
Adam