static int RunDebug(DebugOptions options) { var engine = new DesktopEngine(); var midiAccess = MidiAccessManager.Default; Console.WriteLine("MIDI Inputs"); Console.WriteLine("-----------"); Console.WriteLine(""); Console.WriteLine($"\tId\tName"); Console.WriteLine($"-----------------------------------"); foreach (var input in midiAccess.Inputs) { Console.WriteLine($"\t{input.Id}\t{input.Name}"); } Console.WriteLine(); Console.WriteLine("MIDI Outputs"); Console.WriteLine("------------"); Console.WriteLine(""); Console.WriteLine($"\tId\tName"); Console.WriteLine($"-----------------------------------"); foreach (var output in midiAccess.Outputs) { Console.WriteLine($"\t{output.Id}\t{output.Name}"); } return(0); }
static int RunPlay(PlayOptions options) { var engine = new DesktopEngine(); engine.Server.Run(engine); return(0); }
static int RunPlayDirect(PlayDirectOptions options) { var engine = new DesktopEngine(); var midiAccess = MidiAccessManager.Default; foreach (var input in options.MidiInputs) { var foundMidiInput = midiAccess.Inputs.Where(x => x.Name.ToLower().Contains(input.ToLower()) || x.Id.ToLower().Contains(input.ToLower())).FirstOrDefault(); if (foundMidiInput == null) { Console.Error.WriteLine($"Did not find any MIDI input partially matching '{input}'."); return(1); } else { Console.WriteLine($"Using MIDI input '{foundMidiInput.Name}'."); } engine.MidiInputs.Add(midiAccess.OpenInputAsync(foundMidiInput.Id).Result); } foreach (var output in options.MidiOutputs) { var foundMidiOutput = midiAccess.Outputs.Where(x => x.Name.ToLower().Contains(output.ToLower()) || x.Id.ToLower().Contains(output.ToLower())).FirstOrDefault(); if (foundMidiOutput == null) { Console.Error.WriteLine($"Did not find any MIDI output partially matching '{output}'."); return(1); } else { Console.WriteLine($"Using MIDI output'{foundMidiOutput.Name}'."); } engine.MidiOutputs.Add(midiAccess.OpenOutputAsync(foundMidiOutput.Id).Result); } var scoreFilePath = options.FilePath; FileStream fileStream = null; ScoreBuilder scoreBuilder = null; Score score = null; Dictionary <byte, byte> noteVelocityMap = new Dictionary <byte, byte>(); foreach (var index in Enumerable.Range(0, 128)) { noteVelocityMap.Add((byte)index, 0); } var lastNoteOnVelocities = new Queue <decimal>(3); if (scoreFilePath != null) { if (!File.Exists(options.FilePath)) { Console.Error.WriteLine($"Could not find sheet music file '{options.FilePath}'."); return(1); } try { fileStream = new FileStream(scoreFilePath, FileMode.Open); } catch (Exception ex) { Console.Error.WriteLine($"Could not open sheet music file path {scoreFilePath}: {ex}"); } if (fileStream == null) { return(2); } try { scoreBuilder = new ScoreBuilder(fileStream); score = scoreBuilder.Build(); } catch (Exception ex) { Console.Error.WriteLine($"Could not build sheet music score from file path {scoreFilePath}: {ex}"); } if (scoreBuilder == null || score == null) { return(3); } else { Console.WriteLine($"Successfully loaded sheet music at {scoreFilePath}."); } engine.Interpreter.SetScore(score, scoreFilePath); } foreach (var midiInput in engine.MidiInputs) { midiInput.MessageReceived += (object sender, MidiReceivedEventArgs e) => { switch (e.Data[0]) { case MidiEvent.NoteOff: { var pitch = e.Data[1]; Console.WriteLine($"Off (actual): {pitch}"); engine.Interpreter.Input(new NoteRelease() { Pitch = pitch }); } break; case MidiEvent.NoteOn: { var pitch = e.Data[1]; var velocity = e.Data[2]; var isSimulatedNoteOff = velocity == 0; var isRequestedPitchAlreadyAtZeroVelocity = noteVelocityMap[pitch] == 0; var isRequestedNoteOffAlreadyNoteOff = isRequestedPitchAlreadyAtZeroVelocity; var shouldNoteOffBeNoteOn = isSimulatedNoteOff && isRequestedNoteOffAlreadyNoteOff; if (shouldNoteOffBeNoteOn) { var averagePreviousNoteVelocities = (byte)(lastNoteOnVelocities.Average()); Console.WriteLine($"<!!! CAUGHT !!!> On (simulated) {counter1++}: {pitch} at {String.Format("{0:0%}", averagePreviousNoteVelocities / 127.0)}"); noteVelocityMap[pitch] = averagePreviousNoteVelocities; engine.Interpreter.Input(new NotePress() { Pitch = pitch, Velocity = averagePreviousNoteVelocities }); } else { /** The Yamaha P-45 sends Note Off messages as Note On * messages with zero velocity. */ var isNoteOnActuallyNoteOff = velocity == 0; if (isNoteOnActuallyNoteOff) { Console.WriteLine($"Off (simulated) {counter1++}: {pitch} at {String.Format("{0:0%}", velocity / 127.0)} <---> (Last) {String.Format("{0:0%}", (byte)noteVelocityMap[pitch] / 127.0)}"); noteVelocityMap[pitch] = 0; engine.Interpreter.Input(new NoteRelease() { Pitch = pitch }); } else { lastNoteOnVelocities.Enqueue(velocity); if (lastNoteOnVelocities.Count > 3) { lastNoteOnVelocities.Dequeue(); } noteVelocityMap[pitch] = velocity; Console.WriteLine($"On {counter1++}: {pitch} at {String.Format("{0:0%}", velocity / 127.0)}"); engine.Interpreter.Input(new NotePress() { Pitch = pitch, Velocity = velocity }); } } } break; case MidiEvent.CC: { var pedalKind = e.Data[1]; var position = (byte)(127 - e.Data[2]); // Console.WriteLine($"Pedal {counter1++}: {String.Format("{0:0%}", position / 127.0)}"); engine.Interpreter.Input(new PedalChange() { Pedal = PedalKind.Sustain, Position = position }); } break; } }; } engine.Interpreter.Output += (IPianoEvent e) => { foreach (var output in engine.MidiOutputs) { switch (e) { case PedalChange pedal: output.Send(new byte[] { MidiEvent.CC, pedal.Pedal switch { PedalKind.UnaCorda => 67, PedalKind.Sostenuto => 66, PedalKind.Sustain => 64, _ => 64 }, (byte)(pedal.Position) }, 0, 3, 0); break;