Closed BoxOfClicks closed 3 years ago
Hi,
Yes, structure of the project has been changed. I'll write detailed instruction later.
You need native binaries placed along with the main DLL of the library. Please take latest master files from here: https://github.com/melanchall/drywetmidi/releases/download/v6.0.0/DryWetMIDI.6.0.0-bin-native.zip. Extract the archive and place the files near the main DLL.
Please let me know if it works.
Thank you so much for the fast response @melanchall. I spent yesterday investigating and getting things working. With your instructions I was able to quickly get things compiling - many thanks for that - unfortunately no matter what I tried, Unity would crash on enter play mode (2021.1.21). For now we've switched to RtMidi. Is it correct to assume that DWM is also constrained by Windows in the way that a device can only be used exclusively?
@BoxOfClicks On Windows devices can be used exclusively only, yes. It's not a "feature" of DWM, that's how MIDI works on Windows. On macOS there is no such limitation so devices can be used by DWM from different applications.
Can you please provide information for me so I can investigate the problem:
Thanks
Thanks @melanchall. I can try and create a simple repro project possibly later today. In the meantime, the code was a simple modified version of this example:
https://melanchall.github.io/drywetmidi/articles/devices/Input-device.html
In a Monobehaviour Start() method:
var allMidiDevices = InputDevice.GetAll(); foreach(var d in allMidiDevices) { d.EventReceived += HandleMidiMessage; d.StartEventsListening(); //This call causes a crash to desktop }
Obviously also with the HandleMidiMessage defined and a call to dispose the input devices on destroy.
@BoxOfClicks I've just checked this code with Unity 2021.1.26f1:
void Start()
{
Debug.Log("A");
var allMidiDevices = InputDevice.GetAll();
Debug.Log("B");
foreach (var d in allMidiDevices)
{
Debug.Log($"Setup device '{d.Name}'");
d.EventReceived += HandleMidiMessage;
Debug.Log("Start events listening");
d.StartEventsListening(); //This call causes a crash to desktop
Debug.Log("C");
}
}
and got valid log messages without crash:
A
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:9)
B
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:12)
Setup device 'LoopBe Internal MIDI'
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:15)
Start events listening
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:17)
C
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:19)
Setup device 'Game Controller 1'
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:15)
Start events listening
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:17)
C
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:19)
Setup device 'MIDI A'
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:15)
Start events listening
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:17)
C
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:19)
Setup device 'MIDI B'
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:15)
Start events listening
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:17)
C
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:19)
Setup device 'MIDI C'
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:15)
Start events listening
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:17)
C
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:19)
Setup device 'Keyboard'
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:15)
Start events listening
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:17)
C
UnityEngine.Debug:Log (object)
Test:Start () (at Assets/Test.cs:19)
So it seems all works fine within DryWetMIDI.
I can try and create a simple repro project possibly later today.
It would be great. Also can you please catch exactly what device leads to crash? Maybe you can use my code to log all actions?
And one more question: are you really need to listen all devices in the system?
Thanks
Well, I've got some strange things, BUT they appear only if disposing is omitted. So I really need your project to see how you work with devices.
@BoxOfClicks Any news on sample project? :)
I've added Using in Unity article which shows example of simple script with devices. Please see how they should be obtained and disposed. I've tested the script within Unity and all works fine.
@BoxOfClicks I'm closing the issue since there is no info from you. Feel free to reopen if you can provide sample project which can reproduce your problem.
I'm using it in Visual Scripting in a custom unit. and get the same exception, albeit I'm not using any output device. Is this intended?
using Unity.VisualScripting;
using UnityEngine;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Interaction;
using System;
using Melanchall.DryWetMidi.Multimedia;
public class MidiPlayer : Unit
{
[DoNotSerialize]
public ValueInput fileNameInput;
[DoNotSerialize]
public ControlInput inputTrigger;
[DoNotSerialize]
public ControlInput playTrigger;
[DoNotSerialize]
public ControlOutput outputTrigger;
[DoNotSerialize]
public ControlOutput noteTrigger;
[DoNotSerialize]
public ValueOutput notesCountOutput;
[DoNotSerialize]
public ValueOutput noteOutput;
[DoNotSerialize]
private MidiFile midiFile;
[DoNotSerialize]
private NotePlaybackData currentNote;
private Playback playback;
protected override void Definition()
{
inputTrigger = ControlInput("", (flow) =>
{
var file = flow.GetValue<string>(fileNameInput);
midiFile = MidiFile.Read(Application.streamingAssetsPath + "/" + file);
//resultValue = flow.GetValue<string>(myValueA) + flow.GetValue<string>(myValueB) + "!!!";
return outputTrigger;
});
playTrigger = ControlInput("play", (flow) =>
{
//if (midiFile == null) return noteTrigger;
playback = midiFile.GetPlayback();
playback.NoteCallback += delegate (NotePlaybackData rawNoteData, long rawTime, long rawLength, TimeSpan playbackTime)
{
currentNote = rawNoteData;
return rawNoteData;
};
try
{
playback.Start();
}
catch (Exception ex)
{
Debug.LogWarning(ex);
}
return noteTrigger;
});
outputTrigger = ControlOutput("");
noteTrigger = ControlOutput("note played");
fileNameInput = ValueInput<string>("fileName", string.Empty);
notesCountOutput = ValueOutput<int>("notes count", (flow) => {
var notes = midiFile.GetNotes();
return notes.Count;
});
noteOutput = ValueOutput<NotePlaybackData>("current note", (flow) => {
return currentNote;
});
//We need fileName to be set to let the unit process
Requirement(fileNameInput, inputTrigger);
//Requirement(myValueB, inputTrigger); //To display that we need the myValueB value to be set to let the unit process
Succession(inputTrigger, outputTrigger); //To display that the input trigger port input will exits at the output trigger port exit. Not setting your succession also grays out the connected nodes but the execution is still done.
Succession(playTrigger, noteTrigger);
Assignment(playTrigger, noteOutput);
Assignment(inputTrigger, notesCountOutput);//To display the data that is written when the inputTrigger is triggered to the result string output.
}
protected override void BeforeUndefine()
{
base.BeforeUndefine();
playback.NoteCallback = null;
playback.Dispose();
}
}
Hi @schuster-rainer,
Is this intended?
Yes, because you use Playback
class. By default it uses HighPrecisionTickGenerator
(you can read more here). It's a timer that can fire at interval of 1 ms. Such high-precision timer is platform-dependent and thus it is implemented via native layer provided by _Melanchall_DryWetMidiNative64.dll (.dylib) file. So you just need to follow the instruction (please see point 3 in the list there).
Of course you can override default behavior and use your own tick generator implementation. In this case it will be up to you how to implement it, native libraries of DryWetMIDI won't be required. But DryWetMIDI tries to provide the best implementation (in terms of CPU and power usage) for each supported platform (right now, high-precision tick generator supported on Windows and macOS).
Apologies if I'm missing something basic but after importing DWM into a Unity project I'm receiving the following error. I wonder if it's something to do with the recent changes to e.g. CommonApi? Any thoughts or ideas I could test are much appreciated.