public void openFile(string fileName) { try { currentMidiFile = new MidiSequence(fileName); titleText.Text = Path.GetFileNameWithoutExtension(fileName); doRebuild(); } catch (Exception) { filesize.ForeColor = Color.Red; filesize.Text = fileName + ": これはMIDIじゃないよ"; } }
private void SaveAsButton_Click(object sender, EventArgs e) { var res = SaveMidiFile.ShowDialog(this); if (DialogResult.OK == res) { var f = _file; if (ResampleUpDown.Value != _file.TimeBase) { f = f.Resample(unchecked ((short)ResampleUpDown.Value)); } var trks = new List <MidiSequence>(f.Tracks.Count); for (int ic = TrackList.Items.Count, i = 0; i < ic; ++i) { if (TrackList.CheckedItems.Contains(TrackList.Items[i])) { trks.Add(f.Tracks[i]); } } var mf = new MidiFile(1, f.TimeBase); if (!MergeTracksCheckBox.Checked) { foreach (var tr in trks) { mf.Tracks.Add(_ProcessTrack(tr)); } } else { mf.Tracks.Add(_ProcessTrack(MidiSequence.Merge(trks))); } using (var stm = File.OpenWrite(SaveMidiFile.FileName)) { mf.WriteTo(stm); } } }
public void LoadMIDI(string path) { try { using (System.IO.Stream inputStream = System.IO.File.OpenRead(path)) { sequence = MidiSequence.Open(inputStream); } var lbl = (Label)GetNode("SC/Label"); lbl.Text = (string)sequence.ToString(); Stop(); //Stop the player and clear the channels. //Set the MIDI sequence for the player. player.sequence = sequence; Clock.Reset(sequence.Tracks.Count); //RESET THE CLOCK. This properly sets the number of tracks for the clock to check. AudioStreamGenerator streamGenerator = (AudioStreamGenerator)player.Stream; player.parser.ParseSequence(sequence, streamGenerator.MixRate); } catch (MidiParser.MidiParserException e) { GD.Print("WARNING: Encountered a problem parsing MIDI.\n", e.ToString()); } }
public static void WriteMidiFile(MidiSequence sequence, string filename) { MidiFileFormatWriter mffw = new MidiFileFormatWriter(filename); using (mffw) { // Write magic number mffw.WriteString(MidiFileFormat.midiFileMarker, false); // Write header size mffw.WriteUInt32(MidiFileFormat.midiFileHeaderLength); // Write header mffw.WriteUInt16(MidiFileFormat.midiFileVersion); mffw.WriteUInt16((ushort)sequence.Tracks.Count); mffw.WriteUInt16((ushort)((sequence.PPQN > 0) ? sequence.PPQN : (sequence.SMPTETempo | 0x8000))); // Write out the tracks for (int t = 0; t < sequence.Tracks.Count; t++) { WriteMidiTrack(mffw, sequence.Tracks[t]); } } }
public void TotalTime() { var sequence = MidiSequence.Import(MIDI_for_whom_the_bell_tolls); //MediaPlayer: 4m54s = 294s //totalTime/120 = number of music beats var musicTime = (4 * 60) + 54; long maxTotalTime = 0; foreach (var track in sequence.GetTracks()) { long totalTime = 0; foreach (MidiEvent midiEvent in track.Events) { totalTime += midiEvent.DeltaTime; } if (totalTime > maxTotalTime) { maxTotalTime = totalTime; } //(60.0/120.0) = time of one beat (120bpm) var trackTime = (totalTime / sequence.Division) * (60.0 / sequence.Division); Assert.LessOrEqual(trackTime, musicTime); //Trace.TraceInformation(string.Format("Track {0}: {1} seconds", // ((SequenceTrackName)track.Events[0]).Text, // (totalTime / sequence.Division) * (60.0 / sequence.Division))); } var maxTrackTime = (maxTotalTime / sequence.Division) * (60.0 / sequence.Division); Assert.AreEqual(musicTime, maxTrackTime); }
static bool EventsRemain(MidiSequence sequence, int ch) { return(eventPos[ch] <= sequence.Tracks[ch].Events.Count - 1); }
public void ParseSequence(MidiSequence s, double sample_rate = 44100.0) { //First MIDI track should always contain the relevant tempo data. We need to process this data to build a tempo map between ticks, //Translate each tick to a frame position, and push an event to a stack located at the given frame of our lookup dictionary. if (s.Tracks.Count == 0) { return; } eventsAtPosition.Clear(); ActionSet.Clear(); //Initial tempo is == 120bpm int beatLen = 480000; //beat length, in microseconds. Set by tempo events. int divider = s.TicksPerBeatOrFrame > 0? s.TicksPerBeatOrFrame : 48; //Beat divider. Use this with beatLen to calculate a tick length. //Tick length in sample frames. Calculated based on sample_rate * beatLen/ticksPerBeat/1000000. //Useful to determine how many frames we can get away with skipping without worrying about an event miss. double tickLen = 441; TempoMap tempoMap = new TempoMap(); //List of frame positions for every tick. Use to stuff events into position by looking for closest index int frameOffset = 0; //Update this every new tempo event found to account for shifts in deltas. The max value is ~12h at 48000hz. //Build the tempo map. foreach (MidiSharp.Events.MidiEvent m_event in s.Tracks[0]) { if (!(m_event is TempoMetaMidiEvent)) { continue; } var ev = (TempoMetaMidiEvent)m_event; //Calculate the frames for each tick leading up to this next tempo event. Events on tick 0 will skip this and immediately update tempo. //Once the tempo map is built, each tick index will return a corresponding frame until the end of track 0. Any tick indices beyond //the list size should be calculated based on the last known tempo. //TODO: When going through all track events and their deltas, should we extend the list further? Or will precalculating the // event map, while slower initially, make keeping the tempo map completely unnecessary? for (int i = 0; i < ev.DeltaTime; i++) { //Round to the nearest frame. tempoMap.Add((int)Math.Round(frameOffset + i * tickLen)); } //Update the frame offset to the frame where this event should exist. frameOffset = frameOffset + (int)Math.Round(ev.DeltaTime * tickLen); //Now update the actual tempo for the next operation. beatLen = ev.Value; tickLen = (sample_rate * beatLen) / (double)divider / 1000000.0; //Tick length in frames } // Now that all events on track 0 are processed, update the tempo map with the last known tempo value so we can calculate frames // for the rest of the events on the other tracks should their delta offset exceed the last tempo event. tempoMap.tickLen = tickLen; tempoMap.frameOffset = frameOffset; //Now, iterate through all tracks and events and push them to the event map. // foreach (MidiTrack track in s.Tracks) //Assume enumerator moves in order... for (int t = 1; t < s.Tracks.Count; t++) //Assume enumerator moves in order... { MidiTrack track = s.Tracks[t]; int offset = 0; for (int i = 0; i < track.Events.Count; i++) { var ev = track.Events[i]; var frame = tempoMap.CalcFrame((int)(ev.DeltaTime + offset)); List <MidiSharp.Events.MidiEvent> list; if (eventsAtPosition.ContainsKey(frame)) //Make a new list if this key needs it. { list = eventsAtPosition[frame]; } else { list = new List <MidiSharp.Events.MidiEvent>(); eventsAtPosition.Add(frame, list); } //Add the event to the list at this frame position. TODO: organize by MIDI channel? list.Add(ev); offset += (int)ev.DeltaTime; //Move up the delta timer. } } }
MidiFile _ProcessFile() { // first we clone the file to be safe // that way in case there's no modifications // specified in the UI we'll still return // a copy. var result = _file.Clone(); // transpose it if specified if (0 != TransposeUpDown.Value) { result = result.Transpose((sbyte)TransposeUpDown.Value, WrapCheckBox.Checked, !DrumsCheckBox.Checked); } // resample if specified if (ResampleUpDown.Value != _file.TimeBase) { result = result.Resample(unchecked ((short)ResampleUpDown.Value)); } // compute our offset and length in ticks or beats/quarter-notes var ofs = OffsetUpDown.Value; var len = LengthUpDown.Value; if (0 == UnitsCombo.SelectedIndex) // beats { len = Math.Min(len * _file.TimeBase, _file.Length); ofs = Math.Min(ofs * _file.TimeBase, _file.Length); } switch (StartCombo.SelectedIndex) { case 1: ofs += result.FirstDownBeat; break; case 2: ofs += result.FirstNoteOn; break; } // nseq holds our patch and timing info var nseq = new MidiSequence(); if (0 != ofs && CopyTimingPatchCheckBox.Checked) { // we only want to scan until the // first note on // we need to check all tracks so // we merge them into mtrk and scan // that var mtrk = MidiSequence.Merge(result.Tracks); var end = mtrk.FirstNoteOn; if (0 == end) // break later: { end = mtrk.Length; } var ins = 0; for (int ic = mtrk.Events.Count, i = 0; i < ic; ++i) { var ev = mtrk.Events[i]; if (ev.Position >= end) { break; } var m = ev.Message; switch (m.Status) { // the reason we don't check for MidiMessageMetaTempo // is a user might have specified MidiMessageMeta for // it instead. we want to handle both case 0xFF: var mm = m as MidiMessageMeta; switch (mm.Data1) { case 0x51: // tempo case 0x54: // smpte if (0 == nseq.Events.Count) { nseq.Events.Add(new MidiEvent(0, ev.Message.Clone())); } else { nseq.Events.Insert(ins, new MidiEvent(0, ev.Message.Clone())); } ++ins; break; } break; default: // check if it's a patch change if (0xC0 == (ev.Message.Status & 0xF0)) { if (0 == nseq.Events.Count) { nseq.Events.Add(new MidiEvent(0, ev.Message.Clone())); } else { nseq.Events.Insert(ins, new MidiEvent(0, ev.Message.Clone())); } // increment the insert count ++ins; } break; } } // set the track to the loop length nseq.Events.Add(new MidiEvent((int)len, new MidiMessageMetaEndOfTrack())); } // see if track 0 is checked var hasTrack0 = TrackList.GetItemChecked(0); // slice our loop out of it if (0 != ofs || result.Length != len) { result = result.GetRange((int)ofs, (int)len, CopyTimingPatchCheckBox.Checked, false); } // normalize it! if (NormalizeCheckBox.Checked) { result = result.NormalizeVelocities(); } // scale levels if (1m != LevelsUpDown.Value) { result = result.ScaleVelocities((double)LevelsUpDown.Value); } // create a temporary copy of our // track list var l = new List <MidiSequence>(result.Tracks); // now clear the result result.Tracks.Clear(); for (int ic = l.Count, i = 0; i < ic; ++i) { // if the track is checked in the list // add it back to result if (TrackList.GetItemChecked(i)) { result.Tracks.Add(l[i]); } } if (0 < nseq.Events.Count) { // if we don't have track zero we insert // one. if (!hasTrack0) { result.Tracks.Insert(0, nseq); } else { // otherwise we merge with track 0 result.Tracks[0] = MidiSequence.Merge(nseq, result.Tracks[0]); } } // next adjust the tempo if (_file.Tempo != (double)TempoUpDown.Value) { result = result.AdjustTempo((double)TempoUpDown.Value); } // stretch the result. we do this // here so the track lengths are // correct and we don't need ofs // or len anymore if (1m != StretchUpDown.Value) { result = result.Stretch((double)StretchUpDown.Value, AdjustTempoCheckBox.Checked); } // if merge is checked merge the // tracks if (MergeTracksCheckBox.Checked) { var trk = MidiSequence.Merge(result.Tracks); result.Tracks.Clear(); result.Tracks.Add(trk); } _dirty = false; _reseekDirty = false; return(result); }
public void CheckTheFirstChord() { var sequence = MidiSequence.Import(MIDI_for_whom_the_bell_tolls); var guitar1Track = sequence.GetTracks()[2]; var listOfChords = new List <List <NoteOn> >(); List <NoteOn> chordNotes = null; bool isPause = false; foreach (MidiEvent midiEvent in guitar1Track.Events) { if (midiEvent is NoteOn) { var note = (NoteOn)midiEvent; if (note.DeltaTime > 0) { if ((chordNotes != null) && (chordNotes.Count > 0)) { listOfChords.Add(chordNotes); } chordNotes = new List <NoteOn>(); isPause = (note.Velocity == 0); //used for the next notes of the acord (same time) } if ((note.Velocity > 0) && (!isPause)) { chordNotes.Add(note); } } } //start on 1680 from the beggining { var chord = listOfChords[0]; Assert.AreEqual(3, chord.Count); Assert.AreEqual(1680, chord[0].DeltaTime); Assert.AreEqual(100, chord[0].Velocity); Assert.AreEqual("C#4", MidiEvent.GetNoteName(chord[0].Note)); Assert.AreEqual("F#4", MidiEvent.GetNoteName(chord[1].Note)); Assert.AreEqual("F#3", MidiEvent.GetNoteName(chord[2].Note)); } //start on 40 after the last event { var chord = listOfChords[1]; Assert.AreEqual(3, chord.Count); Assert.AreEqual(40, chord[0].DeltaTime); Assert.AreEqual(100, chord[0].Velocity); Assert.AreEqual("C#4", MidiEvent.GetNoteName(chord[0].Note)); Assert.AreEqual("F#4", MidiEvent.GetNoteName(chord[1].Note)); Assert.AreEqual("F#3", MidiEvent.GetNoteName(chord[2].Note)); } //and so on... /* * track.Events.Add(new NoteOn(1680, 1, "C#4", 100)); * track.Events.Add(new NoteOn(0, 1, "F#4", 100)); * track.Events.Add(new NoteOn(0, 1, "F#3", 100)); * track.Events.Add(new NoteOn(40, 1, "F#3", 0)); * track.Events.Add(new NoteOn(0, 1, "F#4", 0)); * track.Events.Add(new NoteOn(0, 1, "C#4", 0)); * track.Events.Add(new NoteOn(0, 1, "C#4", 100)); * track.Events.Add(new NoteOn(0, 1, "F#4", 100)); * track.Events.Add(new NoteOn(0, 1, "F#3", 100)); * track.Events.Add(new NoteOn(40, 1, "F#3", 0)); * track.Events.Add(new NoteOn(0, 1, "F#4", 0)); * track.Events.Add(new NoteOn(0, 1, "C#4", 0)); * track.Events.Add(new NoteOn(40, 1, "C#4", 100)); * track.Events.Add(new NoteOn(0, 1, "F#4", 100)); * track.Events.Add(new NoteOn(0, 1, "F#3", 100)); */ }
public void LoadFile_Check10Tracks() { var sequence = MidiSequence.Import(MIDI_for_whom_the_bell_tolls); Assert.AreEqual(10, sequence.NumberOfTracks); }
/// <summary> /// Creates a notechart from the specified midi path and the actual charttype /// (i.e. ExpertSingle from notes.mid). Due to the overhead necessary to /// parse a midi file. I am going to cram all midi->chart operations into /// one function call. /// This function uses the Toub midi parser which is much faster than Sanford, /// but will throw an exception on certian midi files. /// </summary> /// <param name="chartSelection"> /// The information on which particular notechart to use. /// </param> /// <param name="chartInfo">The metadata on the chart.</param> /// <param name="BPMChanges">The list of BPM changes for this chart.</param> /// <returns> /// A filled out Notechart containing the needed information from the *.mid file. /// </returns> public static Notes ParseMidiInformationToub(ChartSelection chartSelection, Info chartInfo, List <BPMChange> BPMChanges) { Notes notechartToReturn = new Notes(); notechartToReturn.instrument = chartSelection.instrument; notechartToReturn.difficulty = chartSelection.difficulty; // The following two switch's are used to get the proper midi terminology for // the selected track and difficulty. string instrumentPart = null; string greenKey = null; string redKey = null; string yellowKey = null; string blueKey = null; string orangeKey = null; switch (chartSelection.instrument) { case "Single": instrumentPart = "PART GUITAR"; break; case "DoubleGuitar": instrumentPart = "PART GUITAR COOP"; break; case "DoubleBass": instrumentPart = "PART BASS"; break; case "Drums": instrumentPart = "PART DRUMS"; break; default: instrumentPart = "PART GUITAR"; break; } switch (chartSelection.difficulty) { case "Expert": greenKey = "C8"; redKey = "C#8"; yellowKey = "D8"; blueKey = "D#8"; orangeKey = "E8"; break; case "Hard": greenKey = "C7"; redKey = "C#7"; yellowKey = "D7"; blueKey = "D#7"; orangeKey = "E7"; break; case "Medium": greenKey = "C6"; redKey = "C#6"; yellowKey = "D6"; blueKey = "D#6"; orangeKey = "E6"; break; case "Easy": greenKey = "C5"; redKey = "C#5"; yellowKey = "D5"; blueKey = "D#5"; orangeKey = "E5"; break; default: greenKey = "C8"; redKey = "C#8"; yellowKey = "D8"; blueKey = "D#8"; orangeKey = "E8"; break; } MidiSequence mySequence = MidiSequence.Import(chartSelection.directory + "\\notes.mid"); MidiTrack[] myTracks = mySequence.GetTracks(); chartInfo.resolution = mySequence.Division; MidiTrack trackToUse = new MidiTrack(); uint totalTickValue = 0; // Go through each event in the first track (which contains the BPM changes) // and parse the resulting string. for (int i = 0; i < myTracks[0].Events.Count; i++) { Toub.Sound.Midi.MidiEvent currEvent = myTracks[0].Events[i]; string eventString = currEvent.ToString(); string[] splitEventString = eventString.Split('\t'); // Since ticks are stored relative to each other (e.g. 300 ticks // until next note), we must maintain the total tick amout. totalTickValue += Convert.ToUInt32(splitEventString[1]); if (splitEventString[0] == "Tempo") { // In midi files, bpm chages are stored as "microseconds per quarter note" // and must be converted to BPM, and then into the non decimal format the game // uses. double currBPMDouble = 60000000 / Convert.ToDouble(splitEventString[3]); uint BPMToAdd = (uint)(currBPMDouble * 1000); BPMChanges.Add(new BPMChange(totalTickValue, BPMToAdd)); } } trackToUse = new MidiTrack(); // Find the specified instrument's track foreach (MidiTrack currTrack in myTracks) { string trackHeader = currTrack.Events[0].ToString(); string[] splitHeader = trackHeader.Split('\t'); // -If we come across a "T1 GEMS" track, we're in GH1 territory. // -GH2/FoF has both PART BASS and PART RHYTHM (one or the other depending // on the chart). if (((splitHeader[3] == instrumentPart) || (splitHeader[3] == "T1 GEMS")) || ((splitHeader[3] == "PART RHYTHM") && (instrumentPart == "PART BASS"))) { trackToUse = currTrack; } } totalTickValue = 0; uint currTickValue = 0; Note currNote = new Note(); bool blankNote = true; // Scan through and record every note specific to the selected difficulty for (int i = 0; i < trackToUse.Events.Count; i++) { string currEvent = trackToUse.Events[i].ToString(); string[] splitEvent = currEvent.Split('\t'); currTickValue = Convert.ToUInt32(splitEvent[1]); totalTickValue += currTickValue; // We need to specify wether a note is blank or not so we don't add // blank notes from other difficulties into the chart, but if we have // a filled out note, any nonzero tick value means we are moving to a // new note, so we must cut our ties and add this note to the chart. if ((currTickValue != 0) && !blankNote) { notechartToReturn.notes.Add(currNote); currNote = new Note(); blankNote = true; } // The "0x64" I think means "not was hit." There is another // set of notes that use "0x00" that all appear slightly after // the "0x64" notes. if ((splitEvent[0] == "NoteOn") && (splitEvent[4] != "0x00")) { // Only consider notes within the octave our difficulty is in. if ((splitEvent[3] == greenKey) || (splitEvent[3] == redKey) || (splitEvent[3] == yellowKey) || (splitEvent[3] == blueKey) || (splitEvent[3] == orangeKey)) { // If it's a new note, we need to setup the tick value of it. if (blankNote) { currNote.tickValue = totalTickValue; blankNote = false; } if (splitEvent[3] == greenKey) { currNote.addNote(0); } else if (splitEvent[3] == redKey) { currNote.addNote(1); } else if (splitEvent[3] == yellowKey) { currNote.addNote(2); } else if (splitEvent[3] == blueKey) { currNote.addNote(3); } else if (splitEvent[3] == orangeKey) { currNote.addNote(4); } } } } return(notechartToReturn); }
static void ComplexRecordingDemo() { using (var idev = MidiDevice.Inputs[0]) { // TODO: currently this doesn't let you // change the tempo in the middle of recording // match these two variables to your input rate short timeBase = 480; var microTempo = MidiUtility.TempoToMicroTempo(120); // track 0 - meta track for tempo info var tr0 = new MidiSequence(); // our seq for recording var seq = new MidiSequence(); // compute our timing based on current microTempo and timeBase var ticksusec = microTempo / (double)timeBase; var tickspertick = ticksusec / (TimeSpan.TicksPerMillisecond / 1000) * 100; var pos = 0; // set this to _PreciseUtcNowTicks in order // to start recording now. Otherwise it will // not record until the first message is // recieved: var startTicks = 0L; using (var odev = MidiDevice.Outputs[0]) { // hook up the delegate idev.Input += delegate(object s, MidiInputEventArgs ea) { // initialize start ticks with the current time in ticks if (0 == startTicks) { startTicks = _PreciseUtcNowTicks; } // compute our current MIDI ticks var midiTicks = (int)Math.Round((_PreciseUtcNowTicks - startTicks) / tickspertick); // pass through to play odev.Send(ea.Message); // HACK: technically the sequence isn't threadsafe but as long as this event // is not reentrant and the MidiSequence isn't touched outside this it should // be fine seq.Events.Add(new MidiEvent(midiTicks - pos, ea.Message)); // this is to track our old position // so we can compute deltas pos = midiTicks; }; // open the input device idev.Open(); // open the output device odev.Open(); // add our tempo to the beginning of track 0 tr0.Events.Add(new MidiEvent(0, new MidiMessageMetaTempo(microTempo))); // start listening idev.Start(); Console.Error.WriteLine("Recording started."); // wait Console.Error.WriteLine("Press any key to stop recording..."); Console.ReadKey(); // stop the buffer and flush any pending events idev.Stop(); idev.Reset(); } // create termination track var endTrack = new MidiSequence(); var len = seq.Length; // comment the following to terminate // without the trailing empty score: len = unchecked ((int)((_PreciseUtcNowTicks - startTicks) / tickspertick)); endTrack.Events.Add(new MidiEvent(len, new MidiMessageMetaEndOfTrack())); // terminate the tracks tr0 = MidiSequence.Merge(tr0, endTrack); seq = MidiSequence.Merge(seq, endTrack); // build a type 1 midi file var mf = new MidiFile(1, timeBase); // add both tracks mf.Tracks.Add(tr0); mf.Tracks.Add(seq); // now stream the file to the output // just grab the first output stream using (var stm = MidiDevice.Streams[0]) { // open it stm.Open(); // merge the tracks for playback seq = MidiSequence.Merge(mf.Tracks); // set the stream timebase stm.TimeBase = mf.TimeBase; // start the playback stm.Start(); Console.Error.WriteLine("Press any key to exit..."); // if we weren't looping // we wouldn't need to // hook this: stm.SendComplete += delegate(object s, EventArgs e) { // loop stm.Send(seq.Events); }; // kick things off stm.Send(seq.Events); // wait for exit Console.ReadKey(); } } }
private string makeTitle(MidiSequence.MidiTrack track) { string inst = track.InstrumentName; if (inst == null || inst.Length == 0) { inst = "_パート #" + track.TrackNumber; } else { inst += "_" + track.TrackNumber; } return this.titleText.Text + "_" + inst; }
private void addTrack(MidiSequence.MidiTrack track) { string title = makeTitle(track); TabPage p = addTabPage(title, track.toMoEAbc()); p.Tag = track; }
MidiSequence _ProcessTrack(MidiSequence trk) { var ofs = (int)OffsetUpDown.Value; var len = (int)LengthUpDown.Value; if (0 == UnitsCombo.SelectedIndex) // beats { len = (int)Math.Min(Math.Ceiling(len * (decimal)_file.TimeBase), _file.Length); ofs = (int)Math.Min(Math.Ceiling(ofs * (decimal)_file.TimeBase), _file.Length); } switch (StartCombo.SelectedIndex) { case 1: ofs += _file.FirstDownBeat; break; case 2: ofs += _file.FirstNoteOn; break; } switch (StartCombo.SelectedIndex) { case 1: ofs += _file.FirstDownBeat; break; case 2: ofs += _file.FirstNoteOn; break; } if (0 != ofs && CopyTimingPatchCheckBox.Checked) { var end = trk.FirstNoteOn; if (0 == end) { end = trk.Length; } var trk2 = trk.GetRange(ofs, len); var ins = 0; for (int ic = trk.Events.Count, i = 0; i < ic; ++i) { var ev = trk.Events[i]; if (ev.Position >= end) { break; } var m = ev.Message; switch (m.Status) { case 0xFF: var mm = m as MidiMessageMeta; switch (mm.Data1) { case 0x51: case 0x54: trk2.Events.Insert(ins, ev.Clone()); ++ins; break; } break; default: if (0xC0 == (ev.Message.Status & 0xF0)) { trk2.Events.Insert(ins, ev.Clone()); ++ins; } break; } } trk = trk2; } else { if (trk.Length != len || 0 != ofs) { trk = trk.GetRange(ofs, len); } } if (1m != StretchUpDown.Value) { trk = trk.Stretch((double)StretchUpDown.Value, AdjustTempoCheckBox.Checked); } return(trk); }
static void Main(string[] args) { double toneDistance = 1; double beatDistance = 1; double n; List <string> lines = new List <string>(); lines.Add("INSERT PUNCHHOLE"); string path; double note; double ticks; double time; if (args.Length == 0) { Console.WriteLine("Usage: WintergatanLaserMIDI <filename.mid> [Tone Distance (mm)] [Beat Distance (mm)]"); Console.WriteLine("\tfilename.mid = MIDI file for which to generate code"); Console.WriteLine("\tTone Distance = Defines the distance between every note-pitch."); Console.WriteLine("\tBeat Distance = Defines how far away the beats are from each other."); Console.WriteLine(); return; } if (!File.Exists(args[0])) { Console.WriteLine("Error: file {0} not found", args[0]); return; } path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\" + args[0] + ".scr"; path = new Uri(path).LocalPath; if (File.Exists(path)) { File.Delete(path); } if (args.Length == 2 && !string.IsNullOrEmpty(args[1])) { if (double.TryParse(args[1], out n)) { toneDistance = n; } else { Console.WriteLine("Tone Distance must be a number!"); return; } } if (args.Length == 3 && !string.IsNullOrEmpty(args[2])) { if (double.TryParse(args[2], out n)) { beatDistance = n; } else { Console.WriteLine("Beat Distance must be a number!"); return; } } try { MidiSequence sequence = MidiSequence.Open(File.OpenRead(args[0])); ticks = (double)sequence.Division; foreach (MidiTrack track in sequence) { track.Events.ConvertDeltasToTotals(); foreach (MidiEvent ev in track.Events) { if (ev.GetType().ToString() == "MidiSharp.Events.Voice.Note.OnNoteVoiceMidiEvent") { NoteVoiceMidiEvent nvme = ev as NoteVoiceMidiEvent; note = (double)nvme.Note; time = (double)ev.DeltaTime; Console.WriteLine(((time / ticks) * beatDistance) + "," + ((note - 60.0d) * toneDistance)); if (lines.Count >= 2) { lines.Add(" " + ((time / ticks) * beatDistance) + "," + ((note - 60.0d) * toneDistance) + ",0 1 1 0"); } else { lines.Add(((time / ticks) * beatDistance) + "," + ((note - 60.0d) * toneDistance) + ",0 1 1 0"); } } } } System.IO.File.WriteAllLines(path, lines); } catch (Exception exc) { Console.Error.WriteLine("Error: {0}", exc.Message); } }
private void PreviewButton_Click(object sender, EventArgs e) { if ("Stop" == PreviewButton.Text) { if (null != _previewThread) { _previewThread.Abort(); _previewThread.Join(); _previewThread = null; } PreviewButton.Text = "Preview"; return; } var f = _file; if (ResampleUpDown.Value != _file.TimeBase) { f = _file.Resample(unchecked ((short)ResampleUpDown.Value)); } var trks = new List <MidiSequence>(f.Tracks.Count); for (int ic = TrackList.Items.Count, i = 0; i < ic; ++i) { if (TrackList.CheckedItems.Contains(TrackList.Items[i])) { trks.Add(f.Tracks[i]); } } var trk = MidiSequence.Merge(trks); var ofs = (int)OffsetUpDown.Value; var len = (int)LengthUpDown.Value; if (0 == UnitsCombo.SelectedIndex) // beats { len = (int)Math.Min(Math.Ceiling(len * (decimal)f.TimeBase), f.Length); ofs = (int)Math.Min(Math.Ceiling(ofs * (decimal)f.TimeBase), f.Length); } switch (StartCombo.SelectedIndex) { case 1: ofs += f.FirstDownBeat; break; case 2: ofs += f.FirstNoteOn; break; } if (0 != ofs && CopyTimingPatchCheckBox.Checked) { var end = trk.FirstNoteOn; if (0 == end) { end = trk.Length; } var trk2 = trk.GetRange(ofs, len); var ins = 0; for (int ic = trk.Events.Count, i = 0; i < ic; ++i) { var ev = trk.Events[i]; if (ev.Position >= end) { break; } var m = ev.Message; switch (m.Status) { case 0xFF: var mm = m as MidiMessageMeta; switch (mm.Data1) { case 0x51: case 0x54: trk2.Events.Insert(ins, ev.Clone()); ++ins; break; } break; default: if (0xC0 == (ev.Message.Status & 0xF0)) { trk2.Events.Insert(ins, ev.Clone()); ++ins; } break; } } trk = trk2; } else { if (trk.Length != len || 0 != ofs) { trk = trk.GetRange(ofs, len); } } if (1m != StretchUpDown.Value) { trk = trk.Stretch((double)StretchUpDown.Value, AdjustTempoCheckBox.Checked); } if (null != _previewThread) { _previewThread.Abort(); _previewThread.Join(); _previewThread = null; } PreviewButton.Text = "Stop"; _previewThread = new Thread(() => { trk.Preview(f.TimeBase, 0, true); }); _previewThread.Start(); }
public static MidiSequence ParseMidiFile(string filename) { MidiFileFormatReader mffr = new MidiFileFormatReader(filename); using (mffr) { // // Verify magic number // string marker = mffr.ReadString(4); if (marker != MidiFileFormat.midiFileMarker) { throw new FormatException(string.Format("Invalid marker for file {0}, expected {1} found {2}", filename, MidiFileFormat.midiFileMarker, marker)); } // Verify header size uint headerLength = mffr.ReadUInt32(); if (headerLength != MidiFileFormat.midiFileHeaderLength) { throw new FormatException(string.Format("Invalid header length for file {0}, expected {1} found {2}", filename, MidiFileFormat.midiFileHeaderLength, headerLength)); } // // Parse header // // // The 6 next bytes are 16-bits values, defining // the format, #tracks, and ppqn // uint midiVersion = mffr.ReadUInt16(); // Midi version (0, 1, 2) uint numberOfTracks = mffr.ReadUInt16(); // Number of tracks uint resolution = mffr.ReadUInt16(); // PPQN uint ppqn = 0; uint smpteTempo = 0; // // ppqn: Pulse per quarter note: The number of tics // per quarter notes. The length of a tic is the // tempo (length of a quarter note) divided by ppqn. // // // if ppqn is < 0, then it is NOT a ppqn, it is a // tempo in SMTPE format (??) // if (0 != (resolution & 0x8000)) { ppqn = 0; smpteTempo = resolution & 0x7FFF; } else { ppqn = resolution; smpteTempo = 0; } // // Create the midi sequence // MidiSequence result = new MidiSequence(midiVersion, ppqn, smpteTempo, filename); // // Create and parse the tracks // for (uint t = 0; t < numberOfTracks; t++) { result.Tracks.Add(ParseMidiTrack(mffr, t)); } // Return the sequence return(result); } }
static void ComplexStreamingDemo() { // demonstrate streaming a midi file 100 events at a time // this allows you to handle files with more than 64kb // of in-memory events (not the same as "on disk" size) // this replays the events in a loop var midiFile = MidiFile //.ReadFrom(@"..\..\Bohemian-Rhapsody-1.mid"); // > 64kb! .ReadFrom(@"..\..\A-Warm-Place.mid"); //.ReadFrom(@"..\..\GORILLAZ_-_Feel_Good_Inc.mid"); //.ReadFrom(@"..\..\Feel_good_4beatsBass.mid"); //.ReadFrom(@"..\..\THE BEASTIE BOYS.Sabotage.mid"); //.ReadFrom(@"..\..\Peter-Gunn-1.mid"); Console.WriteLine(Path.GetFileName(midiFile.FilePath) + " @ " + midiFile.TimeBase + " PQN"); var tsig = midiFile.TimeSignature; Console.WriteLine("Tempo: " + midiFile.Tempo + " BPM @ " + tsig.Numerator + "/" + tsig.Denominator + " time"); Console.WriteLine("Tracks:"); for (var i = 0; i < midiFile.Tracks.Count; ++i) { var track = midiFile.Tracks[i]; Console.Write("\t"); var name = track.Name; if (string.IsNullOrEmpty(name)) { name = "Track " + i; } Console.WriteLine(name + " \tEvents: " + track.Events.Count); } Console.WriteLine(); // we use 100 events, which should be safe and allow // for some measure of SYSEX messages in the stream // without bypassing the 64kb limit const int EVENT_COUNT = 100; // our current cursor pos int pos = 0; // merge our file for playback var seq = MidiSequence.Merge(midiFile.Tracks); // the number of events in the seq int len = seq.Events.Count; // stores the next set of events var eventList = new List <MidiEvent>(EVENT_COUNT); // just grab the first output stream // should be the wavetable synth using (var stm = MidiDevice.Streams[0]) { // open the stream stm.Open(); // start it stm.Start(); // first set the timebase stm.TimeBase = midiFile.TimeBase; // set up our send complete handler stm.SendComplete += delegate(object sender, EventArgs eargs) { // clear the list eventList.Clear(); // iterate through the next events var next = pos + EVENT_COUNT; for (; pos < next; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; break; } // otherwise add the next event eventList.Add(seq.Events[pos]); } // send the list of events stm.SendDirect(eventList); }; // add the first events for (pos = 0; pos < EVENT_COUNT; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; break; } // otherwise add the next event eventList.Add(seq.Events[pos]); } // send the list of events stm.SendDirect(eventList); // loop until a key is pressed Console.Error.WriteLine("Press any key to exit..."); Console.ReadKey(); // close the stream stm.Close(); } }
static void SimpleRecordingDemo() { MidiFile mf; using (var idev = MidiDevice.Inputs[0]) { using (var odev = MidiDevice.Outputs[0]) { idev.Input += delegate(object s, MidiInputEventArgs e) { // this is so we can pass through and hear // our input while recording odev.Send(e.Message); }; idev.TempoChanged += delegate(object s, EventArgs e) { Console.WriteLine("New Tempo: " + idev.Tempo); }; // open the input // and output idev.Open(); // set our timebase idev.TimeBase = 384; odev.Open(); idev.Start(); // start recording, waiting for input idev.StartRecording(true); // wait to end it Console.Error.WriteLine("Press any key to stop recording..."); Console.ReadKey(); // get our MidiFile from this mf = idev.EndRecording(); // the MIDI file is always two // tracks, with the first track // being the tempo map } } if (null == mf) { return; } // merge for playback var seq = MidiSequence.Merge(mf.Tracks); // now stream the file to the output // just grab the first output stream using (var stm = MidiDevice.Streams[0]) { // open it stm.Open(); // merge the tracks for playback seq = MidiSequence.Merge(mf.Tracks); // set the stream timebase stm.TimeBase = mf.TimeBase; // start the playback stm.Start(); Console.Error.WriteLine("Press any key to exit..."); // if we weren't looping // we wouldn't need to // hook this: stm.SendComplete += delegate(object s, EventArgs e) { // loop stm.Send(seq.Events); }; // kick things off stm.Send(seq.Events); // wait for exit Console.ReadKey(); } }
public Stream Flip(Stream midiFile, int octaveChangeOption) { //Load midi file MidiSequence midi = MidiSequence.Open(midiFile); //The anchor note to flip everything else around float anchorNote = midi.Tracks .Where(t => t.Events.OfType <NoteVoiceMidiEvent>().Any() && t.Events.OfType <NoteVoiceMidiEvent>().Any(n => n.Channel != 9 && n.Channel != 10)) .Select(t => t.Events.OfType <NoteVoiceMidiEvent>().First()) .OrderBy(e => e.DeltaTime).GroupBy(e => e.DeltaTime).First() //Find the first note .Min(e => e.Note); //Order by note number if there are notes playing at the same time int highestNote = midi.Tracks .Where(t => t.Events.OfType <NoteVoiceMidiEvent>().Any(n => n.Channel != 9 && n.Channel != 10)) .SelectMany(t => t.Events.OfType <NoteVoiceMidiEvent>()) .Max(e => e.Note); int lowestNote = midi.Tracks .Where(t => t.Events.OfType <NoteVoiceMidiEvent>().Any(n => n.Channel != 9 && n.Channel != 10)) .SelectMany(t => t.Events.OfType <NoteVoiceMidiEvent>()) .Min(e => e.Note); int octaveChange = 0; //Global octave change required for this sequence octaveChangeOption *= Constants.Octave; bool flipFromMiddle = false; //If octave changes aren't possible, sequence needs to be flipped around the middle //Check if flipping won't make the notes go out of range (0-127) TODO: Test all cases if (anchorNote - lowestNote > highestNote - anchorNote) { //Flipping might make notes go past 127, if so, try to decrease octave float outOfRange = anchorNote + (anchorNote - lowestNote); if (outOfRange > Constants.MaxMidiNote) { while (outOfRange + octaveChange > Constants.MaxMidiNote) { octaveChange -= Constants.Octave; } if (lowestNote + octaveChange < Constants.MinMidiNote) { //Out of range on the other side now, flip everything around the middle note instead and don't change octave flipFromMiddle = true; octaveChange = 0; } } } else { //Flipping might make notes go below 0, if so, try to increase octave float outOfRange = anchorNote - (highestNote - anchorNote); if (outOfRange < Constants.MinMidiNote) { while (outOfRange + octaveChange < Constants.MinMidiNote) { octaveChange += Constants.Octave; } if (highestNote + octaveChange > Constants.MaxMidiNote) { //Out of range on the other side now, flip everything around the middle note instead and don't change octave flipFromMiddle = true; octaveChange = 0; } } } if (flipFromMiddle) //Changing octaves is not possible, flip everything around the middle { anchorNote = (float)(highestNote - lowestNote) / 2; } //Flip all notes foreach (MidiTrack track in midi.Tracks) { IEnumerable <NoteVoiceMidiEvent> noteEvents = track.OfType <NoteVoiceMidiEvent>().Where(n => n.Channel != 9 && n.Channel != 10); if (!noteEvents.Any()) { continue; //Nothing to flip } foreach (NoteVoiceMidiEvent onNoteEvent in noteEvents) { onNoteEvent.Note = (byte)(anchorNote + anchorNote - onNoteEvent.Note + octaveChange + octaveChangeOption); } } //Create flipped MIDI-file Stream flippedMidiStream = new MemoryStream(); midi.Save(flippedMidiStream); flippedMidiStream.Position = 0; return(flippedMidiStream); }
public EasyLedShowLauncher(string filePath = null) { //filePath = @"C:\Users\2425\Desktop\newyear.els"; workingSettings = new SavedSettings(); if (filePath != null) { workingSettings = loadSettings(filePath); } InitializeComponent(); InitializeComports(workingSettings); InitializeMidiDevices(workingSettings); InitializeLaunchDelay(workingSettings); InitializeJINXFile(workingSettings); InitializeJINXProgram(workingSettings); dmxDataPort = new SerialPort(); dmxSubEffects = new int[NR_MAIN_PROGRAMS]; for (int i = 0; i < dmxSubEffects.Length; i++) { if (i == 0) { dmxSubEffects[i] = 4; } else if (i == 1) { dmxSubEffects[i] = 4; } else { dmxSubEffects[i] = 0; } } thresEnabled = new bool[NR_SLIDERS]; thresActive = new bool[NR_SLIDERS]; thresFreeValue = new byte[NR_SLIDERS]; thresOKTime = new long[NR_SLIDERS]; thresActivateTime = new int[NR_SLIDERS]; thresPreValue = new byte[NR_SLIDERS]; thresEnabled[0] = false; thresEnabled[1] = false; thresEnabled[2] = true; thresEnabled[3] = false; thresEnabled[4] = false; thresEnabled[5] = false; thresEnabled[6] = true; thresEnabled[7] = false; for (int i = 0; i < NR_SLIDERS; i++) { if (thresEnabled[i]) { thresFreeValue[i] = 12; thresActivateTime[i] = 2000; thresActive[i] = false; } } timer = new System.Timers.Timer(1000); timer.Elapsed += OnTimedEvent; this.FormClosing += EasyLedShowLauncher_FormClosing; //Auto Launch if (filePath != null) { StartLaunch(); } midiSequence = new MidiSequence(0, 100); }
private ulong t0Tick; // tick time used as reference #endregion #region Constructor public MidiSequencer(MidiSequence sequence) : this(GetEventArrayFromMidiSequence(sequence), sequence.PPQN) { }
public MidiInterpretation(Stream midiStream, INoteSegmentProvider noteSegmentProvider) { MidiFile = MidiSequence.Open(midiStream); TempoCollection = new TempoCollection(MidiFile.TicksPerBeatOrFrame); NoteSegments = new List <NoteSegment>(); // Calculate the absolute times for all events in each track // Also pair note on events with note off events for (int track = 0; track < MidiFile.Tracks.Count; track++) { // Key is a tuple of Channel and Note var onEvents = new Dictionary <(byte Channel, int Note), Queue <MidiEventWithTime <OnNoteVoiceMidiEvent> > >(); // Time in ticks long time = 0; foreach (var midiEvent in MidiFile.Tracks[track].Events) { if (midiEvent.DeltaTime > 0) { time += midiEvent.DeltaTime; } if (midiEvent is TempoMetaMidiEvent tempoEvent) { TempoCollection.AddTempoEvent(time, tempoEvent); } else if (midiEvent is OnNoteVoiceMidiEvent onNote) { var onNoteIdentifier = (onNote.Channel, onNote.Note); if (!onEvents.ContainsKey(onNoteIdentifier)) { onEvents[onNoteIdentifier] = new Queue <MidiEventWithTime <OnNoteVoiceMidiEvent> >(); } // If the Velocity is 0, we are turning off a note using another OnNote (see https://stackoverflow.com/a/43322203/1984712) // Basically, if a NoteOn event is received with a velocity of 0, we effectively have a NoteOff event. if (onNote.Velocity == 0) { if (onEvents.TryGetValue(onNoteIdentifier, out var midiEventQueue)) { NoteSegments.Add(noteSegmentProvider.CreateNoteSegment( TempoCollection, track, midiEventQueue.Dequeue(), // Get the first matching On Event that matches this identifier new MidiEventWithTime <OffNoteVoiceMidiEvent>(time, CreateOffNoteFromOnNote(onNote)) )); } else { System.Diagnostics.Debug.WriteLine(string.Format("OnNote event with Velocity = 0 at [ Channel: {0}; Note: {1} ] is missing a corresponding OnNote event with Velocity > 0.", onNoteIdentifier.Channel, onNoteIdentifier.Note)); } } // Otherwise, queue the note so that an OffNote can match to it else { onEvents[onNoteIdentifier].Enqueue(new MidiEventWithTime <OnNoteVoiceMidiEvent>(time, onNote)); } } else if (midiEvent is OffNoteVoiceMidiEvent offNote) { var offNoteIdentifer = (offNote.Channel, offNote.Note); if (onEvents.TryGetValue(offNoteIdentifer, out var midiEventQueue)) { NoteSegments.Add(noteSegmentProvider.CreateNoteSegment( TempoCollection, track, midiEventQueue.Dequeue(), // Get the first matching On Event new MidiEventWithTime <OffNoteVoiceMidiEvent>(time, offNote)) ); } else { System.Diagnostics.Debug.WriteLine(string.Format("OffNote event at [ Channel: {0}; Note: {1} ] is missing a corresponding OnNote event.", offNoteIdentifer.Channel, offNoteIdentifer.Note)); } } } if (onEvents.Any(e => e.Value.Count > 0)) { System.Diagnostics.Debug.WriteLine("One or more OnNote events weren't paired with an OffNote event."); } } TotalDurationSamples = NoteSegments.Max(segment => segment.StartSample + segment.DurationSamples); }
MidiFile _CreateMidiFile() { var file = new MidiFile(); // we'll need a track 0 for our tempo map var track0 = new MidiSequence(); // set the tempo at the first position track0.Events.Add(new MidiEvent(0, new MidiMessageMetaTempo((double)TempoUpDown.Value))); // compute the length of our loop var len = ((int)BarsUpDown.Value) * 4 * file.TimeBase; // add an end of track marker just so all // of our tracks will be the loop length track0.Events.Add(new MidiEvent(len, new MidiMessageMetaEndOfTrack())); // here we need a track end with an // absolute position for the MIDI end // of track meta message. We'll use this // later to set the length of the track var trackEnd = new MidiSequence(); trackEnd.Events.Add(new MidiEvent(len, new MidiMessageMetaEndOfTrack())); // add track 0 (our tempo map) file.Tracks.Add(track0); // create track 1 (our drum track) var track1 = new MidiSequence(); // we're going to create a new sequence for // each one of the drum sequencer tracks in // the UI var trks = new List <MidiSequence>(BeatsPanel.Controls.Count); foreach (var ctl in BeatsPanel.Controls) { var beat = ctl as BeatControl; // get the note for the drum var note = beat.NoteId; // it's easier to use a note map // to build the drum sequence var noteMap = new List <MidiNote>(); for (int ic = beat.Steps.Count, i = 0; i < ic; ++i) { // if the step is pressed create // a note for it if (beat.Steps[i]) { noteMap.Add(new MidiNote(i * (file.TimeBase / 4), 9, note, 127, file.TimeBase / 4 - 1)); } } // convert the note map to a sequence // and add it to our working tracks trks.Add(MidiSequence.FromNoteMap(noteMap)); } // now we merge the sequences into one var t = MidiSequence.Merge(trks); // we merge everything down to track 1 track1 = MidiSequence.Merge(track1, t, trackEnd); // .. and add it to the file file.Tracks.Add(track1); return(file); }
private void PlayButton_Click(object sender, EventArgs e) { // we use 100 events, which should be safe and allow // for some measure of SYSEX messages in the stream // without bypassing the 64kb limit const int MAX_EVENT_COUNT = 100; const int RATE_TICKS = 500; if ("Stop" == PlayButton.Text) { if (null != _play) // sanity check { _play.Close(); } PlayButton.Text = "Play"; PatternComboBox.Enabled = true; BarsUpDown.Enabled = true; OutputComboBox.Enabled = true; return; } PatternComboBox.Enabled = false; BarsUpDown.Enabled = false; OutputComboBox.Enabled = false; PlayButton.Text = "Stop"; _play = (OutputComboBox.SelectedItem as MidiOutputDevice).Stream; var mf = _CreateMidiFile(); var stm = _play; // our current cursor pos int pos = 0; // for tracking deltas var ofs = 0; // merge our file for playback var seq = MidiSequence.Merge(mf.Tracks); // the number of events in the seq int len = seq.Events.Count; // stores the next set of events var eventList = new List <MidiEvent>(MAX_EVENT_COUNT); // open the stream stm.Open(); // start it stm.Start(); // first set the timebase stm.TimeBase = mf.TimeBase; // set up our send complete handler stm.SendComplete += delegate(object s, EventArgs ea) { try { BeginInvoke(new Action(delegate() { // clear the list eventList.Clear(); mf = _CreateMidiFile(); seq = MidiSequence.Merge(mf.Tracks); ofs = 0; len = seq.Events.Count; // iterate through the next events var next = pos + MAX_EVENT_COUNT; for (; pos < next && ofs <= RATE_TICKS; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; break; } var ev = seq.Events[pos]; ofs += ev.Position; if (ev.Position < RATE_TICKS && RATE_TICKS < ofs) { break; } // otherwise add the next event eventList.Add(ev); } // send the list of events if (MidiStreamState.Closed != stm.State) { stm.SendDirect(eventList); } })); } catch { } }; // add the first events for (pos = 0; pos < MAX_EVENT_COUNT && ofs <= RATE_TICKS; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; break; } var ev = seq.Events[pos]; ofs += ev.Position; if (ev.Position < RATE_TICKS && RATE_TICKS < ofs) { break; } // otherwise add the next event eventList.Add(ev); } // send the list of events stm.SendDirect(eventList); }
public static void doToBMS(MidiSequence wtf, string filename) { var largest_delta = 0; JaiTrackFinal = new MemoryStream(); // Buffer for JaiSeq track var JaiWriter = new BeBinaryWriter(JaiTrackFinal); // Writer into JaiSeq buffer DeltaEnds = new int[wtf.Tracks.Count]; // The deltas that will be smashed onto the end of every track. TotalTrackDeltas = new int[wtf.Tracks.Count]; // The total delta sizes for each track TrackAddresses = new int[wtf.Tracks.Count]; // The addresses that each track is at. TrackLoops = new int[wtf.Tracks.Count]; var rootTrack_OpenTrackPos = JaiWriter.BaseStream.Position; // Store the position where our Open track commands are. for (int i = 0; i < wtf.Tracks.Count; i++) { JaiWriter.Write((byte)JaiSeqEvent.OPEN_TRACK); // Write dummy open track commands JaiWriter.Write((byte)i); // Index writeInt24BE(JaiWriter, 0); // 0-size pointer. } JaiWriter.Write((byte)JaiSeqEvent.TIME_BASE); // Tell JAI to set the timebase JaiWriter.Write((short)wtf.TicksPerBeatOrFrame); // Write timebase value JaiWriter.Write((byte)JaiSeqEvent.TEMPO); // Tell JAI to write the tempo JaiWriter.Write((ushort)Root.Tempo); // Write the tempo value. writePrint(JaiWriter, @"Generated by Xayrga's JAIMaker.\n"); writePrint(JaiWriter, @"JAIMakerInitTrack: "); JaiWriter.Write((byte)JaiSeqEvent.WAIT_8); // Wait JaiWriter.Write((byte)2); // for 0xFFFA ticks writePrint(JaiWriter, @" || DONE. \n"); for (int tridx = 0; tridx < wtf.Tracks.Count; tridx++) { var total_trk_delta = 0; // Total delta for current track var CTrk = wtf.Tracks[tridx]; // Current track object for (int evntid = 0; evntid < CTrk.Events.Count; evntid++) // Iterate through events { var CEvent = CTrk.Events[evntid]; // Current event object total_trk_delta += (int)CEvent.DeltaTime; // Add the event to the total delta for our track if (CTrk.Events[evntid] is MidiSharp.Events.Meta.Text.CuePointTextMetaMidiEvent) { var mevent = (MidiSharp.Events.Meta.Text.CuePointTextMetaMidiEvent)CTrk.Events[evntid]; if (mevent.Text == "JLOOP") { at_least_one_loop = true; // check for loop -- hack } } } TotalTrackDeltas[tridx] = total_trk_delta; // Once we've iterated through all events we store the total track delta. if (total_trk_delta > largest_delta) // We want to know what our highest delta is so we can make all the tracks end at the same time. { largest_delta = total_trk_delta; // So we should store it if it's greater than the last } } for (int trk = 0; trk < wtf.Tracks.Count; trk++) { DeltaEnds[trk] = largest_delta - TotalTrackDeltas[trk]; // Now we know when all the tracks will end at the same time, so we want to sandwhich that onto the end of the track. } var rootTrack_JumpPos = JaiWriter.BaseStream.Position; // Store the position where we want to jump back to writeDelta(JaiWriter, largest_delta); if (at_least_one_loop) { JaiWriter.Write((byte)JaiSeqEvent.JUMP_COND); // Jump to position if JaiWriter.Write((byte)0); // (always) writeInt24BE(JaiWriter, (int)rootTrack_JumpPos); // int24 position. } JaiWriter.Write((byte)JaiSeqEvent.FIN); // Write the finisher for the track, not because it's required but just becase it's standard. for (int TrackID = 0; TrackID < wtf.Tracks.Count; TrackID++) { var MidTrack = wtf.Tracks[TrackID]; TrackAddresses[TrackID] = (int)JaiWriter.BaseStream.Position; // Store track position JaiWriter.Write((byte)0xA4); JaiWriter.Write((byte)0x20); JaiWriter.Write((byte)Root.instrumentBanks[TrackID]); JaiWriter.Write((byte)0xA4); JaiWriter.Write((byte)0x21); JaiWriter.Write((byte)Root.programs[TrackID]); writePrint(JaiWriter, @", T" + TrackID); Stack <byte> voiceStack = new Stack <byte>(8); // JAISeq has 8 voices per track. Queue <byte> notehistory = new Queue <byte>(8); for (byte v = 7; v > 0; v--) // Push all of them to be ready. { Console.WriteLine("PushVoice {0}", v); voiceStack.Push(v); // Push to stack. } byte[] voiceMap = new byte[1024]; // Keeps track of what MIDI notes are currently playing on what voices. for (int ev = 0; ev < MidTrack.Events.Count; ev++) { var cevent = MidTrack.Events[ev]; if (cevent.DeltaTime > 0) // Does the event have any delta? { writeDelta(JaiWriter, (int)cevent.DeltaTime); } if (cevent is MidiSharp.Events.Meta.Text.CuePointTextMetaMidiEvent) { var mevent = (MidiSharp.Events.Meta.Text.CuePointTextMetaMidiEvent)cevent; Console.WriteLine("YEET {0} - {1} ", TrackID, mevent.Text); if (mevent.Text == "JLOOP") { TrackLoops[TrackID] = (int)JaiWriter.BaseStream.Position; at_least_one_loop = true; } } else if (cevent is MidiSharp.Events.Voice.Note.OnNoteVoiceMidiEvent) { var mevent = (MidiSharp.Events.Voice.Note.OnNoteVoiceMidiEvent)cevent; if (voiceMap[mevent.Note] != 0) // This voice is already in use and hasnt been told to stop? { var stopVoice = voiceMap[mevent.Note]; // grab the voice id { voiceStack.Push(stopVoice); JaiWriter.Write((byte)(0x80 + stopVoice)); } } if (voiceStack.Count < 1) // if theres no voice available { for (int i = 0; i < voiceMap.Length; i++) // Iterate through voices table { if (voiceMap[i] > 0) // if a voice is greater than 0 (used) { voiceStack.Push(voiceMap[i]); // Dealloc it (push to voice table) voiceMap[i] = 0; // Set it to 0, it has no pitch, it's been deallocd } } } if (voiceStack.Count > 0) { JaiWriter.Write(mevent.Note); // JAI Note on events are literally just midi notes. var useVoice = voiceStack.Pop(); // Grab next available voice. voiceMap[mevent.Note] = useVoice; // Map it to the pitch JaiWriter.Write(useVoice); // write which JAIVoice it will be using JaiWriter.Write(mevent.Velocity); // Write its velocity. } else { Console.WriteLine("Too many voices {0}", TrackID); } } else if (cevent is MidiSharp.Events.Voice.Note.OffNoteVoiceMidiEvent) { var mevent = (MidiSharp.Events.Voice.Note.OffNoteVoiceMidiEvent)cevent; var stopVoice = voiceMap[mevent.Note]; if (stopVoice == 0) { Console.WriteLine("VOICE LEAK {0}", TrackID); } else { voiceMap[mevent.Note] = 0; voiceStack.Push(stopVoice); //Console.WriteLine("Stop voice {0}",stopVoice); JaiWriter.Write((byte)(0x80 + stopVoice)); } } } // Done with parsting track events. if (DeltaEnds[TrackID] > 0) { writeDelta(JaiWriter, DeltaEnds[TrackID]); // Write finishing delta to make sure all tracks end at the same point. } for (int i = 0; i < voiceMap.Length; i++) { if (voiceMap[i] > 0) { JaiWriter.Write((byte)(0x80 + voiceMap[i])); // stop all notes before song end. Console.WriteLine("=== STOP VOICE END SONG {0}", voiceMap[i]); } } if (TrackLoops[TrackID] > 0) { JaiWriter.Write((byte)JaiSeqEvent.JUMP_COND); // Jump to position if JaiWriter.Write((byte)0); // (always) writeInt24BE(JaiWriter, TrackLoops[TrackID]); // int24 position. } JaiWriter.Write((byte)JaiSeqEvent.FIN); while (JaiWriter.BaseStream.Position % 32 > 0) { JaiWriter.Write((byte)0); } } // Done with parsing tracks. JaiWriter.BaseStream.Position = rootTrack_OpenTrackPos; // We have to update the track opening points that we had before. for (int i = 0; i < wtf.Tracks.Count; i++) { JaiWriter.Write((byte)JaiSeqEvent.OPEN_TRACK); // Write open track command JaiWriter.Write((byte)i); // Index writeInt24BE(JaiWriter, TrackAddresses[i]); } JaiWriter.Flush(); File.WriteAllBytes(filename, ReadToEnd(JaiTrackFinal)); }
private void PreviewButton_Click(object sender, EventArgs e) { if ("Stop" == PreviewButton.Text) { if (null != _play) { _play.Close(); } MidiFileBox.Enabled = true; BrowseButton.Enabled = true; OutputComboBox.Enabled = true; PreviewButton.Text = "Preview"; return; } if (null != _play) { _play.Close(); } MidiFileBox.Enabled = false; BrowseButton.Enabled = false; OutputComboBox.Enabled = false; PreviewButton.Text = "Stop"; if (_dirty) { _processedFile = _ProcessFile(); } var mf = _processedFile; _play = (OutputComboBox.SelectedItem as MidiOutputDevice).Stream; var stm = _play; // we use 100 events, which should be safe and allow // for some measure of SYSEX messages in the stream // without bypassing the 64kb limit const int MAX_EVENT_COUNT = 100; const int RATE_TICKS = 500; // our current cursor pos var pos = 0; // for tracking deltas var ofs = 0; // for tracking the song position var songPos = 0; // merge our file for playback var seq = MidiSequence.Merge(mf.Tracks); var events = seq.Events; // the number of events in the seq var len = events.Count; // stores the next set of events var eventList = new List <MidiEvent>(MAX_EVENT_COUNT); // open the stream stm.Open(); // start it stm.Start(); // first set the timebase stm.TimeBase = mf.TimeBase; // set up our send complete handler stm.SendComplete += delegate(object s, EventArgs ea) { try { BeginInvoke(new Action(delegate() { // clear the list eventList.Clear(); mf = _processedFile; if (_dirty) { if (_reseekDirty) { var time = _processedFile.Tracks[0].GetContext(songPos, _processedFile.TimeBase).Time; _processedFile = _ProcessFile(); songPos = _processedFile.Tracks[0].GetPositionAtTime(time, _processedFile.TimeBase); mf = _processedFile; seq = MidiSequence.Merge(mf.Tracks); events = new List <MidiEvent>(seq.GetNextEventsAtPosition(songPos, true)); len = events.Count; pos = 0; } else { _processedFile = _ProcessFile(); mf = _processedFile; seq = MidiSequence.Merge(mf.Tracks); events = seq.Events; } Visualizer.Sequence = seq; Visualizer.Width = Math.Max(VisualizerPanel.Width, Visualizer.Sequence.Length / 4); } ofs = 0; len = events.Count; // iterate through the next events var next = pos + MAX_EVENT_COUNT; for (; pos < next && ofs <= RATE_TICKS; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; songPos = 0; events = seq.Events; break; } var ev = events[pos]; ofs += ev.Position; songPos += pos; if (ev.Position < RATE_TICKS && RATE_TICKS < ofs) { break; } // otherwise add the next event eventList.Add(ev); } // send the list of events if (MidiStreamState.Closed != stm.State && 0 != eventList.Count) { stm.SendDirect(eventList); } })); } catch { } }; // add the first events for (pos = 0; pos < MAX_EVENT_COUNT && ofs <= RATE_TICKS; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; songPos = 0; events = seq.Events; break; } var ev = events[pos]; ofs += ev.Position; if (ev.Position < RATE_TICKS && RATE_TICKS < ofs) { break; } // otherwise add the next event eventList.Add(ev); } // send the list of events if (0 != eventList.Count) { stm.SendDirect(eventList); } }
/// <summary>Create the demo sequence.</summary> /// <returns>The created midi sequence.</returns> public MidiSequence CreateSequence() { MidiSequence sequence = new MidiSequence(0, 120); MidiTrack track = sequence.AddTrack(); track.Events.Add(new TimeSignature(0, 4, 2, 24, 8)); track.Events.Add(new KeySignature(0, Key.NoFlatsOrSharps, Tonality.Major)); track.Events.Add(new Tempo(0, 416667)); track.Events.Add(new ProgramChange(0, 0, GeneralMidiInstruments.AcousticGrand)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(11, 0, "E6", 60)); track.Events.Add(new Controller(56, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(24, 0, "D#6", 66)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(2, 0, "E6", 0)); track.Events.Add(new NoteOn(75, 0, "E6", 60)); track.Events.Add(new NoteOn(16, 0, "D#6", 0)); track.Events.Add(new NoteOn(72, 0, "D#6", 62)); track.Events.Add(new NoteOn(8, 0, "E6", 0)); track.Events.Add(new NoteOn(77, 0, "E6", 72)); track.Events.Add(new NoteOn(16, 0, "D#6", 0)); track.Events.Add(new NoteOn(64, 0, "B5", 71)); track.Events.Add(new NoteOn(12, 0, "E6", 0)); track.Events.Add(new NoteOn(64, 0, "D6", 85)); track.Events.Add(new NoteOn(19, 0, "B5", 0)); track.Events.Add(new NoteOn(60, 0, "C6", 80)); track.Events.Add(new NoteOn(24, 0, "D6", 0)); track.Events.Add(new NoteOn(51, 0, "A3", 66)); track.Events.Add(new NoteOn(5, 0, "A5", 73)); track.Events.Add(new NoteOn(13, 0, "C6", 0)); track.Events.Add(new Controller(24, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(35, 0, "E4", 70)); track.Events.Add(new NoteOn(51, 0, "A5", 0)); track.Events.Add(new NoteOn(23, 0, "A4", 75)); track.Events.Add(new NoteOn(72, 0, "A4", 0)); track.Events.Add(new NoteOn(0, 0, "C5", 78)); track.Events.Add(new NoteOn(73, 0, "E5", 86)); track.Events.Add(new NoteOn(13, 0, "A3", 0)); track.Events.Add(new NoteOn(42, 0, "E4", 0)); track.Events.Add(new NoteOn(17, 0, "A5", 87)); track.Events.Add(new NoteOn(19, 0, "C5", 0)); track.Events.Add(new NoteOn(14, 0, "E5", 0)); track.Events.Add(new NoteOn(40, 0, "E3", 72)); track.Events.Add(new NoteOn(1, 0, "B5", 84)); track.Events.Add(new Controller(4, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(10, 0, "A5", 0)); track.Events.Add(new Controller(36, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(19, 0, "E3", 0)); track.Events.Add(new NoteOn(5, 0, "E4", 70)); track.Events.Add(new NoteOn(47, 0, "E4", 0)); track.Events.Add(new NoteOn(0, 0, "B5", 0)); track.Events.Add(new NoteOn(9, 0, "G#4", 85)); track.Events.Add(new NoteOn(62, 0, "E5", 82)); track.Events.Add(new NoteOn(66, 0, "G#4", 0)); track.Events.Add(new NoteOn(5, 0, "G#5", 85)); track.Events.Add(new NoteOn(72, 0, "B5", 93)); track.Events.Add(new NoteOn(3, 0, "E5", 0)); track.Events.Add(new NoteOn(29, 0, "G#5", 0)); track.Events.Add(new NoteOn(38, 0, "C6", 78)); track.Events.Add(new NoteOn(4, 0, "A3", 66)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(3, 0, "B5", 0)); track.Events.Add(new Controller(38, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(27, 0, "E4", 72)); track.Events.Add(new NoteOn(76, 0, "A4", 70)); track.Events.Add(new NoteOn(68, 0, "E5", 75)); track.Events.Add(new NoteOn(5, 0, "C6", 0)); track.Events.Add(new NoteOn(46, 0, "A4", 0)); track.Events.Add(new NoteOn(20, 0, "A3", 0)); track.Events.Add(new NoteOn(5, 0, "E4", 0)); track.Events.Add(new NoteOn(3, 0, "E5", 0)); track.Events.Add(new NoteOn(1, 0, "E6", 74)); track.Events.Add(new NoteOn(70, 0, "E6", 0)); track.Events.Add(new NoteOn(2, 0, "D#6", 82)); track.Events.Add(new NoteOn(76, 0, "E6", 90)); track.Events.Add(new NoteOn(6, 0, "D#6", 0)); track.Events.Add(new Controller(4, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new NoteOn(59, 0, "D#6", 86)); track.Events.Add(new NoteOn(14, 0, "E6", 0)); track.Events.Add(new NoteOn(57, 0, "E6", 103)); track.Events.Add(new NoteOn(20, 0, "D#6", 0)); track.Events.Add(new NoteOn(56, 0, "B5", 90)); track.Events.Add(new NoteOn(11, 0, "E6", 0)); track.Events.Add(new NoteOn(66, 0, "D6", 86)); track.Events.Add(new NoteOn(11, 0, "B5", 0)); track.Events.Add(new NoteOn(62, 0, "C6", 85)); track.Events.Add(new NoteOn(8, 0, "D6", 0)); track.Events.Add(new NoteOn(72, 0, "A5", 77)); track.Events.Add(new NoteOn(3, 0, "C6", 0)); track.Events.Add(new NoteOn(1, 0, "A3", 55)); track.Events.Add(new Controller(16, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(52, 0, "E4", 60)); track.Events.Add(new NoteOn(28, 0, "A5", 0)); track.Events.Add(new NoteOn(50, 0, "A4", 77)); track.Events.Add(new NoteOn(63, 0, "C5", 90)); track.Events.Add(new NoteOn(3, 0, "A4", 0)); track.Events.Add(new NoteOn(57, 0, "A3", 0)); track.Events.Add(new NoteOn(14, 0, "E5", 86)); track.Events.Add(new NoteOn(66, 0, "E4", 0)); track.Events.Add(new NoteOn(3, 0, "A5", 82)); track.Events.Add(new NoteOn(3, 0, "C5", 0)); track.Events.Add(new NoteOn(12, 0, "E5", 0)); track.Events.Add(new NoteOn(54, 0, "B5", 89)); track.Events.Add(new NoteOn(3, 0, "E3", 79)); track.Events.Add(new NoteOn(7, 0, "A5", 0)); track.Events.Add(new Controller(2, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new Controller(45, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(14, 0, "E3", 0)); track.Events.Add(new NoteOn(7, 0, "E4", 68)); track.Events.Add(new NoteOn(66, 0, "E4", 0)); track.Events.Add(new NoteOn(11, 0, "G#4", 84)); track.Events.Add(new NoteOn(67, 0, "D5", 82)); track.Events.Add(new NoteOn(11, 0, "B5", 0)); track.Events.Add(new NoteOn(4, 0, "G#4", 0)); track.Events.Add(new NoteOn(68, 0, "C6", 73)); track.Events.Add(new NoteOn(37, 0, "D5", 0)); track.Events.Add(new NoteOn(35, 0, "B5", 69)); track.Events.Add(new NoteOn(69, 0, "B5", 0)); track.Events.Add(new NoteOn(3, 0, "C6", 0)); track.Events.Add(new NoteOn(6, 0, "A3", 60)); track.Events.Add(new NoteOn(2, 0, "A5", 68)); track.Events.Add(new Controller(0, 0, Controllers.EffectControl1Fine, 0)); track.Events.Add(new Controller(49, 0, Controllers.EffectControl1Fine, 127)); track.Events.Add(new NoteOn(29, 0, "E4", 66)); track.Events.Add(new NoteOn(90, 0, "A4", 71)); track.Events.Add(new NoteOn(23, 0, "A5", 0)); track.Events.Add(new NoteOn(5, 0, "A3", 0)); track.Events.Add(new NoteOn(16, 0, "E4", 0)); track.Events.Add(new NoteOn(0, 0, "A4", 0)); track.Events.Add(new NoteOn(130, 0, "E6", 80)); track.Events.Add(new EndOfTrack(130)); return(sequence); }
void _UpdateMidiFile() { var exists = false; try { if (File.Exists(MidiFileBox.Text)) { exists = true; } } catch { } TrackList.Items.Clear(); if (!exists) { TracksLabel.Text = ""; MidiFileBox.ForeColor = Color.Red; _file = null; TrackList.Enabled = false; PreviewButton.Enabled = false; UnitsCombo.Enabled = false; StartCombo.Enabled = false; OffsetUpDown.Enabled = false; LengthUpDown.Enabled = false; StretchUpDown.Enabled = false; MergeTracksCheckBox.Enabled = false; CopyTimingPatchCheckBox.Enabled = false; AdjustTempoCheckBox.Enabled = false; ResampleUpDown.Enabled = false; NormalizeCheckBox.Enabled = false; LevelsUpDown.Enabled = false; TransposeUpDown.Enabled = false; WrapCheckBox.Enabled = false; DrumsCheckBox.Enabled = false; SaveAsButton.Enabled = false; TempoUpDown.Enabled = false; Visualizer.Sequence = null; Visualizer.Size = VisualizerPanel.Size; } else { MidiFileBox.ForeColor = SystemColors.WindowText; using (Stream stm = File.OpenRead(MidiFileBox.Text)) _file = MidiFile.ReadFrom(stm); var i = 0; foreach (var trk in _file.Tracks) { var s = trk.Name; if (string.IsNullOrEmpty(s)) { s = "Track #" + i.ToString(); } TrackList.Items.Add(s, true); ++i; } var sig = _file.TimeSignature; var key = _file.KeySignature; TracksLabel.Text = string.Format(_tracksLabelFormat, sig.Numerator, sig.Denominator, key); TrackList.Enabled = true; PreviewButton.Enabled = true; UnitsCombo.Enabled = true; StartCombo.Enabled = true; OffsetUpDown.Enabled = true; LengthUpDown.Enabled = true; StretchUpDown.Enabled = true; MergeTracksCheckBox.Enabled = true; CopyTimingPatchCheckBox.Enabled = true; AdjustTempoCheckBox.Enabled = true; ResampleUpDown.Enabled = true; NormalizeCheckBox.Enabled = true; LevelsUpDown.Enabled = true; TransposeUpDown.Enabled = true; WrapCheckBox.Enabled = true; DrumsCheckBox.Enabled = true; SaveAsButton.Enabled = true; TempoUpDown.Enabled = true; StretchUpDown.Value = 1; UnitsCombo.SelectedIndex = 0; StartCombo.SelectedIndex = 0; ResampleUpDown.Value = _file.TimeBase; UnitsCombo.SelectedIndex = 0; LengthUpDown.Maximum = _file.Length / (decimal)_file.TimeBase; OffsetUpDown.Maximum = LengthUpDown.Maximum - 1; LengthUpDown.Value = LengthUpDown.Maximum; OffsetUpDown.Value = 0; AdjustTempoCheckBox.Checked = false; MergeTracksCheckBox.Checked = false; NormalizeCheckBox.Checked = false; CopyTimingPatchCheckBox.Checked = true; LevelsUpDown.Value = 1; TransposeUpDown.Value = 0; WrapCheckBox.Checked = false; DrumsCheckBox.Checked = false; TempoUpDown.Value = (decimal)_file.Tempo; _dirty = true; _processedFile = null; Visualizer.Sequence = MidiSequence.Merge(_file.Tracks); Visualizer.Width = Math.Max(VisualizerPanel.Width, Visualizer.Sequence.Length / 4); } }
void Output(OutputType type) { switch (type) { case OutputType.Midi: /*List<MidiEvent> midiEvents = new List<MidiEvent> (); * * double max = 0; * for (int i = 0; i < notes.Count; i++) { * double d = notes[i].duration * 4; * double t1 = notes[i].time * 4; * double t2 = t1 + d; * max = t2 > max ? t2 : max; * * NoteOnEvent note = new NoteOnEvent (Convert.ToInt64 (t1), 1, notes[i].noteNumber + 40, 127, Convert.ToInt32(d)); * midiEvents.Add (note); * } * MidiEvent endMarker = new NoteEvent (Convert.ToInt64(max), 1, MidiCommandCode.StopSequence, 0, 0); * midiEvents.Add (endMarker); * * MidiEventCollection collection = new MidiEventCollection (0, 1); * collection.AddTrack (midiEvents); * * MidiFile.Export ("C:/Users/Jonas/OneDrive/GIP/Proef/Output/midi.mid", collection);*/ MidiTrack track = new MidiTrack(); long maxTime = 0; //string path = "C:/Users/Jonas/Desktop/result.txt"; double previousTime = 0; for (int i = 0; i < notes.Count; i++) { string path = "C:/Users/Jonas/Desktop/result.txt"; File.AppendAllText(path, string.Format("{0} at {1}\n", DecodeNote(notes[i].noteNumber + 44, NoteNotation.Short), notes[i].time)); double d = 1; double t1 = notes[i].time * 20 - previousTime; double t2 = t1 + d; if (t1 < 0) { continue; } previousTime = t2; MidiEvent onEvent = new OnNoteVoiceMidiEvent(Convert.ToInt64(t1), 1, (byte)(44 + notes[i].noteNumber), 63); MidiEvent offEvent = new OffNoteVoiceMidiEvent(Convert.ToInt64(t2), 1, (byte)(44 + notes[i].noteNumber), 0); long t2long = (long)t2; maxTime = (t2long > maxTime) ? t2long : maxTime; track.Events.Add(onEvent); track.Events.Add(offEvent); } MidiEvent endMarker = new EndOfTrackMetaMidiEvent(maxTime); track.Events.Add(endMarker); MidiSequence sequence = new MidiSequence(Format.Zero, 1000); sequence.Tracks.Add(track); FileStream stream = new FileStream("C:/Users/Jonas/OneDrive/GIP/Proef/Output/midi.mid", FileMode.OpenOrCreate); sequence.Save(stream); stream.Close(); break; case OutputType.Audio: double longest = 0; for (int i = 0; i < notes.Count; i++) { double time = notes[i].time + notes[i].duration; longest = Math.Max(time, longest); } double[] newSamples = new double[(int)Math.Ceiling(longest * rawAudio.sampleRate)]; for (int i = 0; i < notes.Count; i++) { int startIndex = (int)Math.Floor(notes[i].time * rawAudio.sampleRate); int endIndex = (int)Math.Floor((notes[i].time + notes[i].duration) * rawAudio.sampleRate); double freq = 110 * Math.Pow(1.059463094359295, notes[i].noteNumber); double amp = notes[i].amplitude; for (int j = startIndex; j < endIndex; j++) { double time = j * rawAudio.SampleLength + notes[i].time; double value = Math.Sin(time * freq * 2 * Math.PI) * amp; //double maxAmp = Math.Min (1, 5 * Math.Min (Math.Abs (time - notes[i].time), Math.Abs (time - (notes[i].time + notes[i].duration)))); newSamples[j] += value; } } for (int i = 0; i < newSamples.Length; i++) { newSamples[i] = Math.Max(Math.Min(1, newSamples[i]), -1); } int avgSpread = 35; double[] smoothSamples = new double[newSamples.Length - avgSpread]; for (int i = 0; i < newSamples.Length - avgSpread; i++) { double tot = 0; for (int j = 0; j < avgSpread; j++) { tot += newSamples[i + j]; } smoothSamples[i] = tot / avgSpread; } Song song = new Song(smoothSamples, rawAudio.channels, rawAudio.sampleRate); // GIPIO.SaveSong (song, GIPIO.outputPath + "test.wav"); producedAudio = song; break; case OutputType.Wavelet: pixels = new uint[allAmps.Count * allAmps[0].Length]; for (int x = 0; x < allAmps.Count; x++) { for (int y = 0; y < allAmps[0].Length; y++) { double i = allAmps[x][y]; // i *= 2; i = Math.Min(1, i); // double r = -2 * (i - 1.0) * (2 * (i - 1.0)) + 1; // double g = -2 * (i - 0.5) * (2 * (i - 0.5)) + 1; // double b = -2 * (i - 0.0) * (2 * (i - 0.0)) + 1; double r, g, b; r = g = b = i; uint red = (uint)Math.Round(r * 255); uint green = (uint)Math.Round(g * 255); uint blue = (uint)Math.Round(b * 255); int index = (allAmps[0].Length - y - 1) * allAmps.Count + x; pixels[index] = (uint)((255 << 24) + (red << 16) + (green << 8) + blue); } } DrawWavelet(); break; } }
private void PlayButton_Click(object sender, EventArgs e) { if ("Stop" == PlayButton.Text) { PlayButton.Text = "Play"; if (null != _outputStream) { _outputStream.Close(); } MidiOutComboBox.Enabled = true; FileTextBox.Enabled = true; FileBrowseButton.Enabled = true; Visualizer.ShowCursor = false; return; } PlayButton.Text = "Stop"; Visualizer.CursorPosition = 0; Visualizer.ShowCursor = true; MidiOutComboBox.Enabled = false; FileTextBox.Enabled = false; FileBrowseButton.Enabled = false; var mf = MidiFile.ReadFrom(FileTextBox.Text); if (null != _outputStream) { // BUG: For some reason recycling the output stream // screws up playback on successive uses. I have had // no luck tracking down why so far. The following // causes the MidiStream class to be recreated // instead of recycled var stm = _outputStream = MidiDevice.Streams[_outputStream.Index]; // we use 100 events, which should be safe and allow // for some measure of SYSEX messages in the stream // without bypassing the 64kb limit const int MAX_EVENT_COUNT = 100; // the lower this is, the more more CPU it takes. // the higher it is, the less accurate the cursor // position will be: const int RATE_TICKS = 10; // our current cursor pos var pos = 0; // for tracking deltas var ofs = 0; // for tracking the song position var songPos = 0; var songTicks = 0; // merge our file for playback var seq = MidiSequence.Merge(mf.Tracks); var events = seq.Events; // the number of events in the seq var len = events.Count; // stores the next set of events var eventList = new List <MidiEvent>(MAX_EVENT_COUNT); // open the stream stm.Open(); if (MidiOutputDeviceVolumeSupport.None != stm.VolumeSupport) { stm.Volume = new MidiVolume((byte)VolumeKnob.Value, (byte)VolumeKnob.Value); } // start it stm.Start(); // first set the timebase stm.TimeBase = mf.TimeBase; // set up our send complete handler stm.SendComplete += delegate(object s, EventArgs ea) { try { BeginInvoke(new Action(delegate() { // clear the list eventList.Clear(); ofs = 0; len = events.Count; // iterate through the next events var next = pos + MAX_EVENT_COUNT; for (; pos < next && ofs <= RATE_TICKS; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; songPos = 0; songTicks = 0; events = seq.Events; break; } var ev = events[pos]; ofs += ev.Position; songTicks += ev.Position; songPos += pos; if (ev.Position < RATE_TICKS && RATE_TICKS < ofs) { break; } // otherwise add the next event eventList.Add(ev); } // send the list of events if (MidiStreamState.Closed != stm.State && 0 != eventList.Count) { stm.SendDirect(eventList); } Visualizer.CursorPosition = songTicks; })); } catch { } }; // add the first events for (pos = 0; pos < MAX_EVENT_COUNT && ofs <= RATE_TICKS; ++pos) { // if it's past the end, loop it if (len <= pos) { pos = 0; songPos = 0; songTicks = 0; events = seq.Events; break; } var ev = events[pos]; ofs += ev.Position; songTicks += ev.Position; if (ev.Position < RATE_TICKS && RATE_TICKS < ofs) { break; } // otherwise add the next event eventList.Add(ev); } Visualizer.CursorPosition = songTicks; // send the list of events if (0 != eventList.Count) { stm.SendDirect(eventList); } } }
static void Main(string[] args) { int[] codeMidi = new int[] { 0, 0, 0, 0, 0, 0, 0 }; if (args.Length != 1) { Console.WriteLine("Usage: TransposeMidi.exe filename.mid steps"); Console.WriteLine(" filename.mid = MIDI file to be transposed"); Console.WriteLine(" steps = Number of steps to transpose, positive or negative"); Console.WriteLine(); return; } string originpath = System.IO.Directory.GetCurrentDirectory(); string[] op = originpath.Split('\\'); originpath = ""; for (int i = 0; i < (op.Length) - 4; i++) { originpath += op[i]; originpath += "\\"; } if (!File.Exists(args[0])) { Console.WriteLine("Error: file {0} not found", args[0]); return; } /* int steps; * if (!int.TryParse(args[1], out steps)) * { * Console.WriteLine("Error: invalid number of steps {0}", args[1]); * return; * }*/ try { MidiSequence sequence; using (Stream inputStream = File.OpenRead(args[0])) { sequence = MidiSequence.Open(inputStream); } for (int i = 0; i < 8; i++) { MidiSequence newSequence = sequence.Trim(1857 * i, 1857 * (i + 1) + 10); // sequence.Transpose(steps); using (Stream outputStream = File.OpenWrite(originpath + @"\\toUnity\\" + i + "\\" + "originmelody." + i + ".trimed.mid.txt")) { newSequence.Save(outputStream); } foreach (MidiTrack track in newSequence) { for (int j = 1; j < track.Events.Count - 1; j = j + 2) { //Console.WriteLine(track.Events[i].ToString().IndexOf("G4")); //Console.WriteLine(track.Events[i].ToString()); //Console.WriteLine(track.Events[i].ToString().Substring(5,7)); char spt = '\t'; string[] spstring = track.Events[j].ToString().Split(spt); switch (spstring[2]) { case "C6": codeMidi[2] = codeMidi[2] + 3; codeMidi[5] = codeMidi[5] + 2; codeMidi[0]++; break; case "D6": codeMidi[3] = codeMidi[3] + 3; codeMidi[6] = codeMidi[6] + 2; codeMidi[1]++; break; case "E6": codeMidi[4] = codeMidi[4] + 3; codeMidi[0] = codeMidi[0] + 2; codeMidi[2]++; break; case "F6": codeMidi[5] = codeMidi[5] + 3; codeMidi[1] = codeMidi[1] + 2; codeMidi[3]++; break; case "G6": codeMidi[6] = codeMidi[6] + 3; codeMidi[2] = codeMidi[2] + 2; codeMidi[4]++; break; case "A6": codeMidi[0] = codeMidi[0] + 3; codeMidi[5] = codeMidi[5] + 2; codeMidi[3]++; break; case "B6": codeMidi[1] = codeMidi[1] + 3; codeMidi[6] = codeMidi[5] + 2; codeMidi[4]++; break; default: break; } //Console.WriteLine(spstring[2]); } int maxValue = codeMidi.Max(); String path = System.IO.Directory.GetCurrentDirectory() + @"\\toUnity\\" + i; DirectoryInfo dirInfo = new DirectoryInfo(path); if (dirInfo.Exists == false) { Directory.CreateDirectory(path); } for (int j = 0; j < 8; j++) { if (codeMidi[j] == maxValue) { //Console.WriteLine(j); List <String> MyFiles = Directory.GetFiles(originpath + @"\Resources\\" + j).ToList(); foreach (string file in MyFiles) { FileInfo mFile = new FileInfo(file); // to remove name collisions if (new FileInfo(originpath + @"\\toUnity\\" + i + "\\" + mFile.Name).Exists == false) { mFile.CopyTo(originpath + @"\\toUnity\\" + i + "\\" + mFile.Name + ".txt"); } } MyFiles.Clear(); MyFiles = null; break; } } System.Array.Clear(codeMidi, 0, 7); } } } /*string outputPath = args[0] + ".transposed.mid"; * using (Stream outputStream = File.OpenWrite(outputPath)) { * sequence.Save(outputStream); * } * Console.WriteLine("Transposed MIDI written to: {0}", outputPath);*/ // catch (Exception exc) { Console.Error.WriteLine("Error: {0}", exc.Message); } }