Custom meta events
Meta events specify non-MIDI information useful to specific applications. As with custom chunks, future or custom meta events may be designed. Format of meta events allows programs which don't know about these new events to skip them without reading process failure. DryWetMIDI allows you to implement custom meta events which can be written to a MIDI file track chunk and be read from it.
For example, let's create an event which will hold an image. Custom meta event must be derived from the MetaEvent and must implement four abstract methods:
Also a class must have a parameterless constructor.
public sealed class ImageEvent : MetaEvent
{
public ImageEvent()
: base()
{
}
public ImageEvent(Image image)
: this()
{
Image = image;
}
public Image Image { get; set; }
protected override void ReadContent(MidiReader reader, ReadingSettings settings, int size)
{
throw new NotImplementedException();
}
protected override void WriteContent(MidiWriter writer, WritingSettings settings)
{
throw new NotImplementedException();
}
protected override int GetContentSize(WritingSettings settings)
{
throw new NotImplementedException();
}
protected override MidiEvent CloneEvent()
{
throw new NotImplementedException();
}
}
Now we implement methods mentioned above. Start from the ReadContent
:
protected override void ReadContent(MidiReader reader, ReadingSettings settings, int size)
{
if (size == 0)
return;
var imageBytes = reader.ReadBytes(size);
var converter = new ImageConverter();
Image = (Image)converter.ConvertFrom(imageBytes);
}
Every meta event contains the size of the event's content. Size is passed to ReadContent
through the size
parameter so we know how many bytes we need to read in order to restore an image.
Now let's implement WriteContent
:
protected override void WriteContent(MidiWriter writer, WritingSettings settings)
{
if (Image == null)
return;
var converter = new ImageConverter();
var imageBytes = (byte[])converter.ConvertTo(Image, typeof(byte[]));
writer.WriteBytes(imageBytes);
}
Now we have to implement GetContentSize
:
protected override int GetContentSize(WritingSettings settings)
{
if (Image == null)
return 0;
var converter = new ImageConverter();
var imageBytes = (byte[])converter.ConvertTo(Image, typeof(byte[]));
return imageBytes.Length;
}
Value returned by this method will be written to the event as its content size.
To support cloning of an event we need to implement CloneEvent
method:
public override MidiEvent CloneEvent()
{
return new ImageEvent(Image?.Clone() as Image);
}
The custom meta event is completely implemented. In order to read and write it we must assign a status byte to the event. You have to pick a value from the [0x5F; 0x7E] range which will be the status byte of your event type. You can get status bytes of standard meta events via MetaEvent.GetStandardMetaEventStatusBytes. See code sample below to know how to read and write custom meta event:
// Define collection of custom meta event types along with
// corresponding status bytes.
var customMetaEventTypes = new EventTypesCollection
{
{ typeof(ImageEvent), 0x5F }
};
// Write an image event to an existing file.
var file = MidiFile.Read("My Great Song.mid");
var trackChunk = file.Chunks.OfType<TrackChunk>().First();
var image = Image.FromFile("My image.jpg");
var imageEvent = new ImageEvent(image);
trackChunk.Events.Add(imageEvent);
file.Write(
"My Great Song.mid",
true,
MidiFileFormat.MultiTrack,
new WritingSettings
{
CustomMetaEventTypes = customMetaEventTypes
});
// Read a MIDI file with ImageEvent inside.
//
// Note that if you don't specify custom meta event through CustomMetaEventTypes
// property of the ReadingSettings it will be read as UnknownMetaEvent.
var updatedFile = MidiFile.Read(
"My Great Song.mid",
new ReadingSettings
{
CustomMetaEventTypes = customMetaEventTypes
});