Skip to content

Commit

Permalink
Fix possible KeyNotFoundException on SMF format 0
Browse files Browse the repository at this point in the history
  • Loading branch information
gocha committed Jun 16, 2018
1 parent ade590f commit 8098f78
Showing 1 changed file with 99 additions and 43 deletions.
142 changes: 99 additions & 43 deletions src/MidiSplit/MidiSplit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace MidiSplit
public class MidiSplit
{
public const string NAME = "MidiSplit";
public const string VERSION = "1.2";
public const string VERSION = "1.2.5";
public const string AUTHOR = "gocha";
public const string URL = "http://github.com/gocha/midisplit";

Expand Down Expand Up @@ -237,6 +237,11 @@ protected struct MTrkChannelParam
public int ProgramNumber;
public int BankNumber;
public int NoteNumber;

public override string ToString()
{
return "(" + this.GetType().Name + ")[MidiChannel: " + MidiChannel + ", ProgramNumber: " + ProgramNumber + ", BankNumber: " + BankNumber + ", NoteNumber: " + NoteNumber + "]";
}
}

protected class MTrkChunkWithInfo
Expand All @@ -249,6 +254,8 @@ protected class MTrkChunkWithInfo

static IEnumerable<MTrkChunk> SplitMidiTrack(MTrkChunk midiTrackIn, bool copySeparatedControllers, IList<int> percMidiChannels, IList<int> percProgChanges)
{
bool verbose = false;

if (percMidiChannels == null)
{
percMidiChannels = new List<int>();
Expand Down Expand Up @@ -351,7 +358,9 @@ static IEnumerable<MTrkChunk> SplitMidiTrack(MTrkChunk midiTrackIn, bool copySep
// save the trigger timing
if (allNotesOff)
{
boundaryEventIndex[midiChannel] = midiEventIndex + 1;
// find the next channel message of the same channel
boundaryEventIndex[midiChannel] = midiEventIndex + 1 + midiEventListIn.Skip(midiEventIndex + 1).TakeWhile(ev =>
!(ev.Message is MidiChannelMessage && ((MidiChannelMessage)ev.Message).MidiChannel == midiChannel)).Count();
}
}
}
Expand Down Expand Up @@ -389,10 +398,25 @@ static IEnumerable<MTrkChunk> SplitMidiTrack(MTrkChunk midiTrackIn, bool copySep

lastTrackMapIndex[midiChannel] = boundaryEventIndex[midiChannel];
}
midiEventMapTo[lastTrackMapIndex[midiChannel]] = channelParam;

try
{
midiEventMapTo.Add(lastTrackMapIndex[midiChannel], channelParam);
}
catch (ArgumentException e)
{
// debug output and fallback
int key = lastTrackMapIndex[midiChannel];
Console.WriteLine(e);
Console.WriteLine(" " + key + " => " + midiEventMapTo[key]);
Console.WriteLine(" is overwritten by " + channelParam);
midiEventMapTo[key] = channelParam;
}
}

boundaryEventIndex[midiChannel] = midiEventIndex + 1;
// find the next channel message of the same channel
boundaryEventIndex[midiChannel] = midiEventIndex + 1 + midiEventListIn.Skip(midiEventIndex + 1).TakeWhile(ev =>
!(ev.Message is MidiChannelMessage && ((MidiChannelMessage)ev.Message).MidiChannel == midiChannel)).Count();
}
else if (channelMessage.Command == MidiChannelCommand.ProgramChange)
{
Expand Down Expand Up @@ -425,12 +449,26 @@ static IEnumerable<MTrkChunk> SplitMidiTrack(MTrkChunk midiTrackIn, bool copySep
channelParam.ProgramNumber = currentProgramNumber[midiChannel];
channelParam.NoteNumber = -1; // will be filled later

midiEventMapTo[boundaryEventIndex[midiChannel]] = channelParam;
try
{
midiEventMapTo.Add(boundaryEventIndex[midiChannel], channelParam);
}
catch (ArgumentException e)
{
// debug output and fallback
int key = boundaryEventIndex[midiChannel];
Console.WriteLine(e);
Console.WriteLine(" " + key + " => " + midiEventMapTo[key]);
Console.WriteLine(" is overwritten by " + channelParam);
midiEventMapTo[key] = channelParam;
}
lastTrackMapIndex[midiChannel] = boundaryEventIndex[midiChannel];
}

// update the trigger timing
boundaryEventIndex[midiChannel] = midiEventIndex + 1;
// find the next channel message of the same channel
boundaryEventIndex[midiChannel] = midiEventIndex + 1 + midiEventListIn.Skip(midiEventIndex + 1).TakeWhile(ev =>
!(ev.Message is MidiChannelMessage && ((MidiChannelMessage)ev.Message).MidiChannel == midiChannel)).Count();
}
}
else if (channelMessage.Command == MidiChannelCommand.ControlChange)
Expand Down Expand Up @@ -463,8 +501,6 @@ static IEnumerable<MTrkChunk> SplitMidiTrack(MTrkChunk midiTrackIn, bool copySep
{
if (midiEventMapTo.ContainsKey(midiEventIndex))
{
//Console.WriteLine("event: " + midiEventIndex + ", channel: " + midiEventMapTo[midiEventIndex].MidiChannel + ", bank: " + midiEventMapTo[midiEventIndex].BankNumber + ", program: " + midiEventMapTo[midiEventIndex].ProgramNumber + ", note: " + midiEventMapTo[midiEventIndex].NoteNumber);

// remove if item is exactly identical to previos one
if (lastChannelParam.HasValue && midiEventMapTo[midiEventIndex].Equals(lastChannelParam.Value))
{
Expand All @@ -480,8 +516,12 @@ static IEnumerable<MTrkChunk> SplitMidiTrack(MTrkChunk midiTrackIn, bool copySep
// create output tracks
foreach (KeyValuePair<int, MTrkChannelParam> aMidiEventMapTo in midiEventMapTo)
{
//Console.WriteLine(String.Format("Event: {0}, Channel: {1}, Bank: {2}, Program: {3}",
// aMidiEventMapTo.Key, aMidiEventMapTo.Value.MidiChannel, aMidiEventMapTo.Value.BankNumber, aMidiEventMapTo.Value.ProgramNumber));
if (verbose)
{
Console.WriteLine(String.Format("[MidiEventMap] Index: {0} => MidiChannel: {1}, BankNumber: {2}, ProgramNumber: {3}, NoteNumber: {4}",
aMidiEventMapTo.Key, aMidiEventMapTo.Value.MidiChannel, aMidiEventMapTo.Value.BankNumber, aMidiEventMapTo.Value.ProgramNumber, aMidiEventMapTo.Value.NoteNumber));
}

if (!trackAssociatedWith.ContainsKey(aMidiEventMapTo.Value))
{
MTrkChunkWithInfo trackInfo = new MTrkChunkWithInfo();
Expand Down Expand Up @@ -531,49 +571,65 @@ static IEnumerable<MTrkChunk> SplitMidiTrack(MTrkChunk midiTrackIn, bool copySep
MTrkChunk targetTrack = null;
bool broadcastToAllTracks = false;

// dispatch message
if (midiEvent.Message is MidiChannelMessage)
if (verbose)
{
MidiChannelMessage channelMessage = midiEvent.Message as MidiChannelMessage;
byte midiChannel = channelMessage.MidiChannel;
Console.Write("[MidiEvent] Index: " + midiEventIndex);
Console.Write(", AbsoluteTime: " + midiEvent.AbsoluteTime);
Console.Write(", MessageType: " + midiEvent.Message.GetType().Name);
if (midiEvent.Message is MidiChannelMessage)
{
MidiChannelMessage channelMessage = midiEvent.Message as MidiChannelMessage;
Console.Write(", MidiChannel: " + channelMessage.MidiChannel);
Console.Write(", Command: " + channelMessage.Command);
Console.Write(", Parameter1: " + channelMessage.Parameter1);
Console.Write(", Parameter2: " + channelMessage.Parameter2);
}
Console.WriteLine();
}

// switch output track if necessary
if (midiEventMapTo.ContainsKey(midiEventIndex))
// switch output track if necessary
if (midiEventMapTo.ContainsKey(midiEventIndex))
{
MTrkChannelParam aMidiEventMapTo = midiEventMapTo[midiEventIndex];
MTrkChunkWithInfo newTrackInfo = trackAssociatedWith[aMidiEventMapTo];
int channel = aMidiEventMapTo.MidiChannel;

MTrkChunkWithInfo oldTrackInfo = null;
if (currentOutputTrackInfo.ContainsKey(channel))
{
MTrkChannelParam aMidiEventMapTo = midiEventMapTo[midiEventIndex];
MTrkChunkWithInfo newTrackInfo = trackAssociatedWith[aMidiEventMapTo];
int channel = aMidiEventMapTo.MidiChannel;
oldTrackInfo = currentOutputTrackInfo[channel];
}

MTrkChunkWithInfo oldTrackInfo = null;
if (currentOutputTrackInfo.ContainsKey(channel))
{
oldTrackInfo = currentOutputTrackInfo[channel];
}
if (oldTrackInfo != newTrackInfo)
{
// switch output track
currentOutputTrackInfo[channel] = newTrackInfo;

if (oldTrackInfo != newTrackInfo)
// copy separated controller values
if (copySeparatedControllers)
{
// switch output track
currentOutputTrackInfo[channel] = newTrackInfo;
// readahead initialization events for new track (read until the first note on)
MidiChannelStatus initStatus = new MidiChannelStatus();
initStatus.DataEntryForRPN = status[channel].DataEntryForRPN;
initStatus.ParseMidiEvents(midiEventListIn.Skip(midiEventIndex).TakeWhile(ev =>
!(ev.Message is MidiChannelMessage && ((MidiChannelMessage)ev.Message).Command == MidiChannelCommand.NoteOn)), (byte)channel);

// copy separated controller values
if (copySeparatedControllers)
{
// readahead initialization events for new track (read until the first note on)
MidiChannelStatus initStatus = new MidiChannelStatus();
initStatus.DataEntryForRPN = status[channel].DataEntryForRPN;
initStatus.ParseMidiEvents(midiEventListIn.Skip(midiEventIndex).TakeWhile(ev =>
!(ev.Message is MidiChannelMessage && ((MidiChannelMessage)ev.Message).Command == MidiChannelCommand.NoteOn)), midiChannel);

status[channel].AddUpdatedMidiEvents(newTrackInfo.Track, newTrackInfo.Status, midiEvent.AbsoluteTime, channel, initStatus);
}
status[channel].AddUpdatedMidiEvents(newTrackInfo.Track, newTrackInfo.Status, midiEvent.AbsoluteTime, channel, initStatus);
}

// save current controller values
if (oldTrackInfo != null)
{
oldTrackInfo.Status = new MidiChannelStatus(status[channel]);
}
// save current controller values
if (oldTrackInfo != null)
{
oldTrackInfo.Status = new MidiChannelStatus(status[channel]);
}
}
}

// dispatch message
if (midiEvent.Message is MidiChannelMessage)
{
MidiChannelMessage channelMessage = midiEvent.Message as MidiChannelMessage;
byte midiChannel = channelMessage.MidiChannel;

// determine output track
targetTrack = currentOutputTrackInfo[midiChannel].Track;
Expand Down

0 comments on commit 8098f78

Please sign in to comment.