Closed eloyer closed 5 months ago
Hi,
Please show full code, especially how you declare and instantiate _outputDevice
.
Here you go:
public string OutputDeviceName = "mydevice";
private OutputDevice _outputDevice;
...
_outputDevice = OutputDevice.GetByName(OutputDeviceName);
_outputDevice.PrepareForEventsSending();
_outputDevice.EventSent += OnEventSent;
NoteEvent noteEvent = new NoteOnEvent();
noteEvent.SetNoteNumber(NoteName.C, 2);
_outputDevice.SendEvent(noteEvent);
...
private void OnEventSent(object sender, MidiEventSentEventArgs e)
{
var midiDevice = (MidiDevice)sender;
Console.WriteLine($"Event sent to '{midiDevice.Name}' at {DateTime.Now}: {e.Event}");
}
Can you please post the entire file content?
Sure, here's the whole thing (above was a somewhat simplified version):
using System;
using System.Linq;
using System.Text;
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Composing;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Multimedia;
using Melanchall.DryWetMidi.Standards;
using Melanchall.DryWetMidi.MusicTheory;
using UnityEngine;
public class DemoScript : MonoBehaviour
{
public string OutputDeviceName = "opertoonist";
private OutputDevice _outputDevice;
private Playback _playback;
private void Start()
{
InitializeOutputDevice();
/*var midiFile = CreateTestFile();
InitializeFilePlayback(midiFile);
StartPlayback();*/
InvokeRepeating("PlayOneNote", 1, 1);
}
private void OnApplicationQuit()
{
Debug.Log("Releasing playback and device...");
if (_playback != null)
{
_playback.NotesPlaybackStarted -= OnNotesPlaybackStarted;
_playback.NotesPlaybackFinished -= OnNotesPlaybackFinished;
_playback.Dispose();
}
if (_outputDevice != null)
_outputDevice.Dispose();
Debug.Log("Playback and device released.");
}
private void InitializeOutputDevice()
{
Debug.Log($"Initializing output device [{OutputDeviceName}]...");
var allOutputDevices = OutputDevice.GetAll();
var allDevicesList = string.Join(Environment.NewLine, allOutputDevices.Select(d => $" {d.Name}"));
if (!allOutputDevices.Any(d => d.Name == OutputDeviceName))
{
Debug.Log($"There is no [{OutputDeviceName}] device presented in the system. Here the list of all device:{Environment.NewLine}{allDevicesList}");
return;
}
Debug.Log($"Here the list of all devices:{Environment.NewLine}{allDevicesList}");
_outputDevice = OutputDevice.GetByName(OutputDeviceName);
_outputDevice.PrepareForEventsSending();
_outputDevice.EventSent += OnEventSent;
Debug.Log($"Output device [{OutputDeviceName}] initialized.");
}
private void PlayOneNote()
{
Debug.Log("play one note");
NoteEvent noteEvent = new NoteOnEvent();
noteEvent.SetNoteNumber(NoteName.C, 2);
_outputDevice.SendEvent(noteEvent);
Invoke("StopNote", 0.25f);
}
private void StopNote()
{
Debug.Log("stop one note");
NoteEvent noteEvent = new NoteOffEvent();
noteEvent.SetNoteNumber(NoteName.C, 2);
_outputDevice.SendEvent(noteEvent);
}
private MidiFile CreateTestFile()
{
Debug.Log("Creating test MIDI file...");
var patternBuilder = new PatternBuilder()
.SetNoteLength(MusicalTimeSpan.Eighth)
.SetVelocity(SevenBitNumber.MaxValue)
.ProgramChange(GeneralMidiProgram.Harpsichord);
foreach (var noteNumber in SevenBitNumber.Values)
{
patternBuilder.Note(Melanchall.DryWetMidi.MusicTheory.Note.Get(noteNumber));
}
var midiFile = patternBuilder.Build().ToFile(TempoMap.Default);
Debug.Log("Test MIDI file created.");
return midiFile;
}
private void InitializeFilePlayback(MidiFile midiFile)
{
Debug.Log("Initializing playback...");
_playback = midiFile.GetPlayback(_outputDevice);
_playback.Loop = true;
_playback.NotesPlaybackStarted += OnNotesPlaybackStarted;
_playback.NotesPlaybackFinished += OnNotesPlaybackFinished;
Debug.Log("Playback initialized.");
}
private void StartPlayback()
{
Debug.Log("Starting playback...");
_playback.Start();
}
private void OnEventSent(object sender, MidiEventSentEventArgs e)
{
var midiDevice = (MidiDevice)sender;
Console.WriteLine($"Event sent to '{midiDevice.Name}' at {DateTime.Now}: {e.Event}");
}
private void OnNotesPlaybackFinished(object sender, NotesEventArgs e)
{
LogNotes("Notes finished:", e);
}
private void OnNotesPlaybackStarted(object sender, NotesEventArgs e)
{
LogNotes("Notes started:", e);
}
private void LogNotes(string title, NotesEventArgs e)
{
var message = new StringBuilder()
.AppendLine(title)
.AppendLine(string.Join(Environment.NewLine, e.Notes.Select(n => $" {n}")))
.ToString();
Debug.Log(message.Trim());
}
}
I don't know what InvokeRepeating
is. Please reaplce this line
InvokeRepeating("PlayOneNote", 1, 1);
with just
PlayOneNote();
and try again.
Also can you confirm you see play one note
line in the debug log?
FYI InvokeRepeating calls a method on a timer. But yes, I've replaced it, and yes, I see play one note
in the log, but nothing is sent and OnEventSent
is not called.
Looks like magic :-)
Well, first of all, I see that you use Console.WriteLine
inside OnEventSent
. What if you change it to Debug.Log
?
If that didn't help, let's go deeper. That's how SendEvent
implemented:
public void SendEvent(MidiEvent midiEvent)
{
ThrowIfArgument.IsNull(nameof(midiEvent), midiEvent);
if (!IsEnabled)
return;
EnsureDeviceIsNotDisposed();
EnsureDeviceIsNotRemoved();
EnsureSessionIsCreated();
EnsureHandleIsCreated();
if (midiEvent is ChannelEvent || midiEvent is SystemCommonEvent || midiEvent is SystemRealTimeEvent)
{
var message = PackShortEvent(midiEvent);
NativeApiUtilities.HandleDevicesNativeApiResult(
OutputDeviceApiProvider.Api.Api_SendShortEvent(_handle.DeviceHandle, message));
OnEventSent(midiEvent);
}
else
{
var sysExEvent = midiEvent as SysExEvent;
if (sysExEvent != null)
SendSysExEvent(sysExEvent);
}
}
private void OnEventSent(MidiEvent midiEvent)
{
EventSent?.Invoke(this, new MidiEventSentEventArgs(midiEvent));
}
So there are following reasons EventSent
is not fired:
SendEvent
is not called actually. But this is wrong since as you've said you see play one note
in the log.IsEnabled
is false
. But it's true
by default and I don't see setting it to false
in your code.As you can see in case of NoteOnEvent
it's guaranteed that EventSent
event will be fired (NoteOnEvent
is a subclass of ChannelEvent
).
Let's do some tests.
First of all, change PlayOneNote
implementation to this:
private void PlayOneNote()
{
Debug.Log("A");
NoteEvent noteEvent = new NoteOnEvent();
noteEvent.SetNoteNumber(NoteName.C, 2);
Debug.Log("B");
_outputDevice.IsEnabled = true;
_outputDevice.SendEvent(noteEvent);
Debug.Log($"SendEvent called on {_outputDevice.Name}");
}
And change OnEventSent
to this:
private void OnEventSent(object sender, MidiEventSentEventArgs e)
{
Debug.Log("EVENT");
}
Please send me the log.
Speaking of magic... problem solved. Thanks for the tip to check Console.WriteLine
; I changed it to Debug.Log
and saw that the events were indeed being triggered. I then used Protokol on the Mac to check that the note events were being sent, and they were, but I noticed that their velocity was 0. I set the velocity to 100 and now everything seems to be working! Perhaps because velocity was 0 nothing was being triggered...
In any case, thanks for your help and for this library, very happy to have discovered it.
Hi, apologies in advance as this seems like it must be something simple. I'm using rtpMIDI to route outgoing MIDI from my Unity app on Windows to a Mac. Even though my rtpMIDI session on Windows doesn't announce itself as an output device to drywetmidi, by modifying the demo scene script I can still get the device by its rtpMIDI session name and route the MIDI file playback to it. This is great.
With the same setup, however, so far I'm not able to send a Note-On event directly to the output device. This is my code:
Nothing happens, and the EventSent handler I've set up on the output device isn't called. Any suggestions would be appreciated!