public void Process(MetaMessage message) { #region Require if (message == null) { throw new ArgumentNullException("message"); } #endregion #region Guard if (message.MetaType != MetaType.Tempo) { if (message.MetaType == MetaType.TimeSignature) { var ts = new TimeSignatureBuilder(message); this.ClocksPerMetronomeClick = ts.ClocksPerMetronomeClick; } return; } #endregion TempoChangeBuilder builder = new TempoChangeBuilder(message); // Set the new tempo. Tempo = builder.Tempo; }
public static MetaMessage BuildMessage(int numerator = 4, int denominator = 4, int clocksPerMetronomeClick = 24, int thirtySecondNotesPerQuarterNote = 8) { var sb = new TimeSignatureBuilder(); sb.Numerator = (byte)numerator; sb.Denominator = (byte)denominator; sb.ClocksPerMetronomeClick = (byte)clocksPerMetronomeClick; sb.ThirtySecondNotesPerQuarterNote = (byte)thirtySecondNotesPerQuarterNote; sb.Build(); return sb.Result; }
public GuitarTimeSignature(GuitarMessageList owner, MidiEvent ev) : base(owner, ev, null, GuitarMessageType.GuitarTimeSignature) { if (ev == null) { SetDownTick(0); } else { SetDownEvent(ev); var builder = new TimeSignatureBuilder((MetaMessage)ev.Clone()); Numerator = builder.Numerator; Denominator = builder.Denominator; ClocksPerMetronomeClick = builder.ClocksPerMetronomeClick; ThirtySecondNotesPerQuarterNote = builder.ThirtySecondNotesPerQuarterNote; } }
private static IMidiMessage Convert(Event e, Track track) { if (e is NoteEvent) { NoteEvent note = (NoteEvent)e; if (note.Type == NoteEvent.EventType.NoteOff) { ChannelMessageBuilder b = new ChannelMessageBuilder(); b.MidiChannel = note.Channel; b.Command = ChannelCommand.NoteOff; b.Data1 = ChannelNoteToMidiPitch(note.Pitch); b.Data2 = ChannelVelocityToMidiVolume(note.Velocity); b.Build(); return b.Result; } else { ChannelMessageBuilder b = new ChannelMessageBuilder(); b.MidiChannel = note.Channel; b.Command = ChannelCommand.NoteOn; b.Data1 = ChannelNoteToMidiPitch(note.Pitch); b.Data2 = ChannelVelocityToMidiVolume(note.Velocity); b.Build(); return b.Result; } } else if (e is TempoEvent) { TempoEvent tempoEvent = (TempoEvent)e; TempoChangeBuilder builder = new TempoChangeBuilder(); // convert BPM to microseconds builder.Tempo = 60000000 / tempoEvent.TempoBpm; builder.Build(); return builder.Result; } else if (e is TimeSignatureEvent) { TimeSignatureEvent timeSignatureEvent = (TimeSignatureEvent)e; TimeSignatureBuilder builder = new TimeSignatureBuilder(); builder.Numerator = (byte)timeSignatureEvent.BeatsPerBar; builder.Denominator = (byte)timeSignatureEvent.BeatValue; builder.ClocksPerMetronomeClick = 24; builder.ThirtySecondNotesPerQuarterNote = 8; builder.Build(); return builder.Result; } else { Debug.Fail("unknown event type " + e.GetType().Name); return null; } }
private void importMIDIToolStripMenuItem_Click(object sender, EventArgs e) { if (importDlg.ShowDialog(this) == System.Windows.Forms.DialogResult.Cancel) return; List<PNote> notes = new List<PNote>(); PNote curNote = PNote.Default; float tempo = 1; float timeSig = 4f; // first, we pull midi data Sequence s = new Sequence(importDlg.FileName); // quickly see if there's a piano track first // and get the tempo as well int piano = -1; for (int it = 0; it < s.Count; it++) { Track t = s[it]; foreach (MidiEvent me in t.Iterator()) { switch (me.MidiMessage.MessageType) { case MessageType.Channel: { ChannelMessage m = (ChannelMessage)me.MidiMessage; if (m.Command == ChannelCommand.ProgramChange) if ((GeneralMidiInstrument)m.Data1 == GeneralMidiInstrument.AcousticGrandPiano) { piano = it; } } break; case MessageType.Meta: { MetaMessage m = (MetaMessage)me.MidiMessage; if (m.MetaType == MetaType.Tempo) tempo = (new TempoChangeBuilder(m)).Tempo; else if (m.MetaType == MetaType.TimeSignature) timeSig = new TimeSignatureBuilder(m).Denominator; } break; } if (piano >= 0) break; } if (piano >= 0) break; } // didn't find one, so just try 0th track anyway if (piano == -1) piano = 0; // now, pull all notes (and tempo) // and make sure it's a channel that has content for (int it = piano; it < s.Count; it++) { Track t = s[it]; int delta = 0; foreach (MidiEvent me in t.Iterator()) { delta += me.DeltaTicks; switch (me.MidiMessage.MessageType) { case MessageType.Channel: { ChannelMessage m = (ChannelMessage)me.MidiMessage; switch (m.Command) { case ChannelCommand.NoteOn: if (curNote.Note != "") { curNote.Length = delta / 1000F; delta = 0; notes.Add(curNote); } curNote.Note = note2Piano(m.Data1); break; } } break; case MessageType.Meta: { MetaMessage m = (MetaMessage)me.MidiMessage; if (m.MetaType == MetaType.Tempo) tempo = (new TempoChangeBuilder(m)).Tempo; } break; } } // make sure we get last note if (curNote.Note != "") { curNote.Length = delta / 1000F; notes.Add(curNote); } // we found a track with content! if (notes.Count > 0) break; } // compress redundant accidentals/octaves char[] notemods = new char[7]; int[] noteocts = new int[7]; for (int i = 0; i < 7; i++) { notemods[i] = 'n'; noteocts[i] = 3; } for (int i = 0; i < notes.Count; i++) { string noteStr = notes[i].Note; int cur_note = noteStr[0] - 0x41; char mod = noteStr[1]; int oct = int.Parse(noteStr.Substring(2)); noteStr = noteStr.Substring(0, 1); if (mod != notemods[cur_note]) { noteStr += new string(mod, 1); notemods[cur_note] = mod; } if (oct != noteocts[cur_note]) { noteStr += oct.ToString(); noteocts[cur_note] = oct; } notes[i] = new PNote(notes[i].Length, noteStr); } // now, we find what the "beat" length should be, // by counting numbers of times for each length, and finding statistical mode Dictionary<float, int> scores = new Dictionary<float, int>(); foreach (PNote n in notes) { if (n.Length != 0) if (scores.Keys.Contains(n.Length)) scores[n.Length]++; else scores.Add(n.Length, 1); } float winner = 1; int score = 0; foreach (KeyValuePair<float, int> kv in scores) { if (kv.Value > score) { winner = kv.Key; score = kv.Value; } } // realign all of them to match beat length for (int i = 0; i < notes.Count; i++) { notes[i] = new PNote(notes[i].Length / winner, notes[i].Note); } // compress chords down for (int i = 0; i < notes.Count; i++) { if (notes[i].Length == 0 && i < notes.Count - 1) { notes[i + 1] = new PNote(notes[i + 1].Length, notes[i].Note + "-" + notes[i + 1].Note); notes.RemoveAt(i); i--; } } // add in time for (int i = 0; i < notes.Count; i++) { float len = notes[i].Length; notes[i] = new PNote(len, notes[i].Note + (len != 1 ? "/" + (1 / len).ToString("0.##") : "")); } // what is the bpm, anyway? int rpm = (int)(28800000 / tempo / winner); // 60 * 1,000,000 * .48 the .48 is because note lengths for some reason midi makes the beat note be .48 long // now, output! string line = ""; string output = ""; int lineCount = 1; foreach (PNote n in notes) { if (line.Length + n.Note.Length + 1 > 51) { output += line.Substring(0, line.Length - 1) + "\r\n"; line = ""; if (lineCount == 50) break; lineCount++; } line += n.Note + ","; } if (line.Length > 0) output += line.Substring(0, line.Length - 1); OutputTxt.Text = "BPM: " + rpm.ToString() + "\r\n" + output; OutputTxt.SelectAll(); }
public List<float> TicksPerQuarter(GeneralMidiInstrument inst, bool throwExceptionIfNotFound = false) { var messages = this.GetOrderedMessages(inst, throwExceptionIfNotFound); var meta = messages .Where(x => x.MidiMessage is MetaMessage) .Select(x => x.MidiMessage as MetaMessage) .Where(x => x.MetaType == MetaType.TimeSignature); var time = new TimeSignatureBuilder(); return null; }
static void Main(string[] args) { bool outputMidiVelocity = false; //var filename = "01-v-drac.s3m"; //S3MFile f = S3MFile.Parse(filename); //var tempo = 60000000 / (f.Header.InitialTempo); //byte numerator = 4; //byte denominator = 4; //var speed = 3; //var skip = 0; var filename = "02-v-bewm.s3m"; S3MFile f = S3MFile.Parse(filename); var tempo = 60000000 / (f.Header.InitialTempo); byte numerator = 4; byte denominator = 4; var speed = 3; var skip = 0; outputMidiVelocity = true; /* var filename = "V-OPTION.S3M"; S3MFile f = S3MFile.Parse(filename); var tempo = 60000000 / (f.Header.InitialTempo); byte numerator = 6; byte denominator = 8; var speed = 3; var skip = 4; */ /* var filename = "V-FALCON.S3M"; S3MFile f = S3MFile.Parse(filename); var tempo = 60000000 / (1 * f.Header.InitialTempo); byte numerator = 4; byte denominator = 4; var speed = 3; var skip = 0; */ /* var filename = "V-CONTRA.IT"; S3MFile f = S3MFile.Parse(filename); var tempo = 60000000 / (1 * f.Header.InitialTempo); byte numerator = 4; byte denominator = 4; var speed = 3; var skip = 0; * */ /* var filename = "V-BLAST.S3M"; S3MFile f = S3MFile.Parse(filename); var tempo = 60000000 / (1 * f.Header.InitialTempo); byte numerator = 4; byte denominator = 4; var speed = 3; var skip = 0;*/ Sequence s = new Sequence(); Track t1 = new Track(); Track t2 = new Track(); Track t3 = new Track(); Track drums = new Track(); Track kick = new Track(); Track timeTrack = new Track(); s.Add(timeTrack); s.Add(t1); s.Add(t2); s.Add(t3); //s.Add(drums); //s.Add(kick); var drumPC = new ChannelMessageBuilder(); drumPC.MidiChannel = 09; drumPC.Command = ChannelCommand.ProgramChange; drumPC.Data1 = 0;// 79 + ce.Instrument; drumPC.Build(); drums.Insert(0, drumPC.Result); kick.Insert(0, drumPC.Result); TimeSignatureBuilder tsb = new TimeSignatureBuilder(); tsb.Numerator = numerator; tsb.Denominator = denominator; tsb.ClocksPerMetronomeClick = 24; tsb.ThirtySecondNotesPerQuarterNote = 8; tsb.Build(); timeTrack.Insert(0, tsb.Result); TempoChangeBuilder tcb = new TempoChangeBuilder(); tcb.Tempo = tempo; tcb.Build(); timeTrack.Insert(0, tcb.Result); var outputPatterns = new int[] { 5, 6, 7}; //var c1 = (from p in f.OrderedPatterns // where patterns.Contains(p.PatternNumber) // select p) // .SelectMany(p => p.Channels) // .Where(c => c.ChannelNumber == 1) // .SelectMany(ce => ce.ChannelEvents); //var c2 = (from p in f.OrderedPatterns // where patterns.Contains(p.PatternNumber) // select p) // .SelectMany(p => p.Channels) // .Where(c => c.ChannelNumber == 2) // .SelectMany(ce => ce.ChannelEvents); //var c3 = (from p in f.OrderedPatterns // where patterns.Contains(p.PatternNumber) // select p) // .SelectMany(p => p.Channels) // .Where(c => c.ChannelNumber == 3) // .SelectMany(ce => ce.ChannelEvents); var patterns = from p in f.OrderedPatterns.Skip(skip) /*where outputPatterns.Contains(p.PatternNumber) */select p; //.SelectMany(p => p.Rows); Dictionary<int, TrackInfo> tracks = new Dictionary<int, TrackInfo>(); tracks.Add(1, new TrackInfo(t1)); tracks[1].Channel = 0; tracks.Add(2, new TrackInfo(t2)); tracks[2].Channel = 1; tracks.Add(3, new TrackInfo(t3)); tracks[3].Channel = 2; var di = new TrackInfo(drums); di.Channel = 9; var mapper = new Func<ChannelEvent, int>(ce => { switch (ce.Instrument) { case 6: return 42; case 8: return 35; case 9: return 38; case 10: return 47; default: Debug.Fail(ce.Instrument.ToString()); return 0; } }); di.NoteMapper = mapper; var kickInfo = new TrackInfo(kick); kickInfo.Channel = 9; kickInfo.NoteMapper = mapper; tracks.Add(4, di); tracks.Add(5, kickInfo); var tick = 0; foreach (var pattern in patterns) { bool breakPattern = false; foreach (var row in pattern.Rows) { //if ((row.RowNumber-1) % 16 == 0) //{ // drums.Insert(tick, kickOn); // drums.Insert(tick + speed, kickOff); //} foreach (var ce in row.ChannelEvents) { if (ce == null) { //Console.Out.WriteLine("skip"); continue; } if (ce.Command == 3) { var modulo = row.RowNumber % 32; if (modulo != 0) { // sad to say, not sure why mod 8 but divide by 16 works var m8 = row.RowNumber % 8; if (m8 == 0) { var restore = tsb.Result; tsb.Numerator = (byte)(row.RowNumber / 16); tsb.Build(); var change = tsb.Result; timeTrack.Insert(tick, change); timeTrack.Insert(tick + 3, restore); } } //Debugger.Break(); // pattern break // figure out the time signature of the pattern up to this point // then go back and insert a time signature change message at the beginning // of the pattern // and another one at the end to revert //var restore = tsb.Result; //tsb.Numerator = 2; //tsb.Build(); //var change = tsb.Result; //timeTrack.Insert(rowStartTick, change); //timeTrack.Insert(tick + 3, restore); breakPattern = true; } if (!tracks.ContainsKey(ce.ChannelNumber)) continue; TrackInfo ti = tracks[ce.ChannelNumber]; if (ce.Note == -1 ) { // skip } else if( ce.Note == 0xFF) { // re-use current note, but change instrument //Console.Out.WriteLine("skip"); } else if (ce.Note == 0xFE) //off { if (ti.LastPitch != -1) { NoteOff(tick, ti); } } else { //if (ce.Volume != -1 && ce.Volume < 32) continue; if (ti.LastPitch != -1) { NoteOff(tick, ti); } //p = ProgramChange(ti.Track, tick, p, ce); var delay = 0; if (ce.Command == 19) { if (208 <= ce.Data && ce.Data <= 214) // HACK: 214 is a guess at the top range of SD commands { delay = ce.Data - 208; Debug.Assert(delay >= 0); } } NoteOn(tick + delay, ti, ce); } } tick += speed; if (breakPattern) { break; } } } foreach (var pair in tracks) { if (pair.Value.LastPitch != -1) { NoteOff(tick, pair.Value); } } foreach (MidiEvent m in drums.Iterator().Take(5)) { if (m.MidiMessage is ChannelMessage) { ChannelMessage cm = (ChannelMessage)m.MidiMessage; Console.Out.WriteLine("{0} {1}", m.AbsoluteTicks, cm.Command); } } s.Save(Path.ChangeExtension(filename, ".mid")); /* var tick = 0; var speed = 3; var lastNote = -1; var p = -1; foreach (var ce in c1) { if (ce == null || ce.Note == -1 || ce.Note == 0xFF) { tick += speed; Console.Out.WriteLine("skip"); continue; } else if (ce.Note == 0xFE) //off { lastNote = NoteOff(t, tick, lastNote); } else { if (lastNote != -1) { lastNote = NoteOff(t, tick, lastNote); } p = ProgramChange(t, tick, p, ce); var delay = 0; if (ce.Command == 19) { if (208 <= ce.Data && ce.Data <= 214) // HACK: 214 is a guess at the top range of SD commands { delay = ce.Data - 208; Debug.Assert(delay >= 0); } } lastNote = NoteOn(t, tick + delay, ce.Note); } tick += speed; } lastNote = NoteOff(t, tick, lastNote); * * */ }