melanchall / drywetmidi

.NET library to read, write, process MIDI files and to work with MIDI devices

Home Page:https://melanchall.github.io/drywetmidi

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

DllNotFoundException on Apple Silicon chips

vorobeyDmitriy opened this issue · comments

Hi!
I've tried to get list of all MIDI devices on my MacBook m1 with .NET 6 but InputDevice.GetAll() throws

Unhandled exception. System.DllNotFoundException: Unable to load shared library 'Melanchall_DryWetMidi_Native64' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(libMelanchall_DryWetMidi_Native64, 0x0001): tried: 'libMelanchall_DryWetMidi_Native64' (no such file), '/usr/local/lib/libMelanchall_DryWetMidi_Native64' (no such file), '/usr/lib/libMelanchall_DryWetMidi_Native64' (no such file), '/Users/dvorobey/RiderProjects/Piano/Piano/bin/Debug/net6.0/libMelanchall_DryWetMidi_Native64' (no such file) at Melanchall.DryWetMidi.Multimedia.CommonApi64.GetApiType() at Melanchall.DryWetMidi.Multimedia.CommonApi64.Api_GetApiType() at Melanchall.DryWetMidi.Multimedia.MidiDevicesSession.GetSessionHandle() at Melanchall.DryWetMidi.Multimedia.MidiDevice.EnsureSessionIsCreated() at Melanchall.DryWetMidi.Multimedia.InputDevice.GetAll() at InputDeviceExample.Program.Main(String[] args) in /Users/dvorobey/RiderProjects/Piano/Piano/Program.cs:line 11
Here is a code that I tried to launch
static void Main(string[] args) { var devices = InputDevice.GetAll(); }
What I doing wrong?

Hi,

Hmm, can you please show me content of the folder where your program built? Can you see Melanchall_DryWetMidi_Native64.dylib there?

Also maybe it's caused by M1 since dylib was built on virtual machine and I suppose it doesn't emulate M1 CPU. Well, right now let's start from the output folder as I stated at the start of the comment.

Screenshot 2022-07-14 at 3 49 48 PM

It's a bin/debug/net6.0 folder

Just for test, if you rename Melanchall_DryWetMidi_Native64.dylib to libMelanchall_DryWetMidi_Native64.dylib and try to run Piano from the output folder (not from IDE!), will it work?

The same result. I renamed Melanchall_DryWetMidi_Native64.dylib to libMelanchall_DryWetMidi_Native64.dylib ran it from terminal "dotnet piano.dll".
Do you have any ideas?

Do you have any ideas?

Unfortunately no :( I need to think how we can diagnose the problem.

Can you please say you have .NET 6 installed for x64 or for Arm64?

Arm64
image

Well, do you work in Visual Studio for Mac? Do you install the library from NuGet?

I'm using JetBrains Rider and I've installed Melanchall.DryWetMidi from the NuGet gallery

Well, it's a kind of mystic for me. So I really need your help. So please let's start from simple steps.

  1. Your .NET version is 6.0.301. I suggest to try to update the version. Can you please completely uninstall .NET 6 and install again from https://dotnet.microsoft.com/en-us/download/dotnet/6.0:
    image.
    Does your program work now?
  2. Take dylib from this archive libMelanchall_DryWetMidi_Native64.zip (I've rebuilt the file with new name, maybe simple renaming doesn't work) and put it to the folder with your csproj. In Rider set properties for the file: Content, Copy always to out folder. Rebuild the project. Does your program work now?
  3. What if you put the file to /usr/lib/? Does your program work now?

If steps above didn't lead to the program working, let's try to start from simple apps. I've prepared three different solutions. Please try each one and tell whether something works or not:

  1. CheckDllImport.zip
  2. CheckDllImportLibPrefix.zip
  3. CheckDllImportLibPrefixAndExtension.zip

I believe I must be encountering the same issue as the original poster. Ah cross platform mysteries, so much fun :-)

  • Followed all the instructions above, including using the .NET Uninstall Tool to start with fresh state, and verifying via terminal that it is the Arm64 version of the SDK that is installed: 6.0.302 (arm64)
  • Ran all three sample projects above. Each one correctly transfers its respective *.dylib file into the bin folder, yet all error out with System.DllNotFoundException.
  • Modern mac OS seems to intentionally prevent pasting to the usr/lib folder
  • Also tried with Preview versions of Visual Studio

In my case I am using Visual Studio for Mac, not Rider.

@ThaddeusChristopher Thanks a lot for the info!

Ah cross platform mysteries, so much fun :-)

Yes...

Well, I need your help a little more thus. I've prepared super simple solution with simple native lib built on this code:

int Foo() { return 123; }

Here the solution: SimpleNativeDllImport.zip. Please try it and let me know if DllNotFoundException still occur.

If you have the same error, we need to go further. Unfortunately I have no possibility to build dylib on M1, so I need your help again.

Please open Xcode, create new Library/Framework (if I remember a template name correctly). choose Plain C/C++ language, paste the code above into the editor and build a dylib. Then put it near the csproj replacing original file (rename it to test.dylib if needed or change library name in the DllImport inside C# code). Does SimpleNativeDllImport works fine now?

I've just built dylib on macOS 12 VM image and it seems it has different binary content. Please check with it too: test.zip. Maybe miracle will happen :)

No joy from from either zip above sadly. Bummer!

The new dylib extracted from "test.zip" just posted gets the same error: System.DllNotFoundException: Unable to load shared library 'test' or one of its dependencies.

Its dylib file size is half that of the others though, that's interesting.


I used Xcode to build the requested dylib file. Note: The Xcode Library template comes with no files, so I made a main.cpp and a matching main.h. Apparently when they build the dylib goes to some far-away folder (can find where this is like so: Product >> Show Build Folder).

Overwriting the sample project's test.dylib with this locally built one results in:
Screen Shot 2022-07-19 at 12 26 12 PM
Sounds like permissions related trouble?

Actually this does tell us one thing: The fact that Apple prevents the dylib file from running proves they at least know that it is there :-)

Not much useful info on file properties (file is not "Locked" at least):
Screen Shot 2022-07-19 at 12 30 54 PM

Had some time to try again. I figured out the goofy security stuff Apple wanted (in Settings >> Security & Privacy, just click to allow what it had been blocking). First try didn't work. Then I found this post and changed the header definition of Foo to use [extern "C"] and indeed now it is able to run as expected:

Foo called: 123
Everything fine! Press any key to exit the program...

So that's something at least :-)

@ThaddeusChristopher Many many thanks!

By the way, you can simplify your Xcode project. You don't need to use C++ there, you can create just single C file (for example, main.c) and write this code in it:

int Foo() { return 123; }

Can you please try to create dylib in this way so we can be sure that all works fine? Also can you please write the version of your Xcode?

I really appreciate that you're spending your time to help me!

Happy to assist! Actually if this project ever goes full cross-platform it could really help some people I know :-)

Without a header you say? This results in Xcode warning: "This old-style function definition is not preceded by a prototype"

But yes it does correctly execute. Am using Xcode 13.4.1 (latest)

Without a header you say?

I mean to have only c file, not cpp, like main.c. I use this many times in Xcode when implemented macOS support.

Well, from what I found on the web, the most correct way is to build a universal dylib, which is in fact a union of two architectures: 86_64 and arm64: https://stackoverflow.com/questions/66078178/xcode-not-building-arm64-apple-silicon-universal-dylib. Can you please build universal test.dylib and send me the file so I can test it on Intel-based VMs? So we can be sure the way works.

Also can you please send me entire folder with Xcode project for test.dylib? I'd like to check if it possible to build multi-architecture dylib on Intel-based VMs within my CI process. Also maybe it's possible to change some settings of the project to have output dylib placed somewhere near the project and not far far away on the disk? Or at least say please where it's located after build so I can find it during CI.

Thank you again!

Yes, I tried the "main.c" approach in a new project and had the experience above (no c++ involved, sorry for not specifying).

Alright, I guess this must be the place to make those changes:
Screen Shot 2022-07-20 at 12 29 09 PM
TestDylib2.zip

See zip attached above. I have utterly no idea what to put in those fields :-)

And here you can see where Xcode drops the dylib. Seems needlessly complicated but hey, it's Apple town...

Screen Shot 2022-07-20 at 12 43 25 PM

Thanks!

Another one experiment from me, heh. Can you please check the new file: test.zip?

I think that did it! Well done! This must be the "universal" dylib format?

qadept@QAs-Mac-mini ~ % /Users/qadept/Downloads/SimpleNativeDllImport/SimpleNativeDllImport/bin/Debug/net6.0/SimpleNativeDllImport ; exit;
Foo called: 123
Everything fine! Press any key to exit the program...

@ThaddeusChristopher Thanks again!

This must be the "universal" dylib format?

No, this is just arm64 version (I suppose :)).

Well, to close the subject about dylib format I've prepared three new solutions:

  1. CheckArm64Dylib.zip
  2. CheckUniversalDylib.zip
  3. CheckX86_64Dylib.zip

On VMs I have access to programs 2 and 3 are executed fine printing the correct number (which I've changed to eliminare possible copy/paste errors). The program 1 reasonably failed since VMs are Intel-based.

Please run all three solutions on your M1 chip and show your output. We expect that programs 1 and 2 will be executed fine and the program 3 will fail on your machine.

Thank you!

No problem!

All went as expected. As before, successful builds were at first blocked by Apple until explicitly allowed inside their Settings >> Security app.

CheckArm64Dylib.zip
Builds and runs without issue

CheckUniversalDylib.zip
Builds and runs without issue

CheckX86_64Dylib.zip
Fails to build (DLL not found)

@ThaddeusChristopher Great! Thank you!

Well, now it's time to update my build processes to produce unversal binaries, and then to run unit tests.

I understand that you've already spent a lot of time helping me and appreciate it, but do you mind if I ask you to run tests on your machine after I finished with changing build processes? I just don't understand right now where I can take Apple Silicon-based VMs to run CI of the library :(

I've switched building of macOS binaries to universal format, all my tests are run successfully. But it's for Intel-based VMs, so it's unfortunately says nothing about how it will work on Apple Silicon. I suppose runtime issues are possible.

So if you agree to run unit tests on your machine, it would be great. But I should to note that complete tests run takes noticeable amount of time. Tests are logically divided into two categories: Core and Multimedia. Each category runs for about 20-25 minutes.

Core tests can be run with your regular work on computer in parallel. But it's desired for Multimedia tests to be run without any user activity on a machine. It's because of the nature of those tests. Many of them checks that specific events occurred within a small amount of time (like 30 or even 15 ms), so any other activity can affect tests and they will fail.

Sure I can let those sit uninterrupted on this station for a bit. Do you have a link to the updated solution I can download and run test for?

@ThaddeusChristopher Thanks for responding! I'll try to give as detailed instruction as possible.

Well, first of all we need to clone the library repo on your machine. Suppose you're in Terminal in folder X. Now run this command:

git clone https://github.com/melanchall/drywetmidi.git

And then

cd drywetmidi
git checkout apple-silicon

Now you're switched to the apple-silicon branch. At this point we need to place the files from Native.zip archive in the X/drywetmidi/DryWetMidi folder.

Then

dotnet build DryWetMidi.Tests/Melanchall.DryWetMidi.Tests.csproj --configuration ReleaseTest --framework net6.0

Core tests

If everything builds OK, we're now ready to run core tests. Supposing you're in X/drywetmidi in Terminal:

dotnet test DryWetMidi.Tests/Melanchall.DryWetMidi.Tests.csproj --blame --no-build --configuration ReleaseTest --filter "(FullyQualifiedName~Melanchall.DryWetMidi.Tests.Core|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Common|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Composing|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Interaction|FullyQualifiedName~Melanchall.DryWetMidi.Tests.MusicTheory|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Standards|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Tools)" --framework net6.0 --verbosity normal

I expect the core set should pass. Since tests there are relying on .NET itself, not on native binaries prepared by me.

Multimedia tests

It's time to have some fun :) First of all we need to place dylib from the SendTestData.zip archive to X/DryWetMidi.Tests/bin/ReleaseTest/net6.0.

Then take please the app from the LoopbackDevice.zip archive. Now we need to open separate Terminal window and run the LoopbackDevice program there:

./LoopbackDevice "MIDI A" "MIDI B" "MIDI C" &

Don't close this Terminal instance, we now have three virtual MIDI ports running within the app.

And now we're ready to run multimedia tests (in the first Terminal window of course):

dotnet test DryWetMidi.Tests/Melanchall.DryWetMidi.Tests.csproj --blame --no-build --configuration ReleaseTest --filter "(FullyQualifiedName~.Multimedia)" --framework net6.0 --verbosity normal

Thank you very much!

Core tests failing with stack overflow. This could be because the "dotnet" terminal command seems to be preferring Visual Studio Preview over the non-preview version also installed. Hence when I ran the "build" function it built using VS for Mac Preview. At a glance I'm uncertain how to re-map the "dotnet" command to the non-preview VS, assuming that is what the issue here is.


qadept@QAs-Mac-mini drywetmidi % dotnet test DryWetMidi.Tests/Melanchall.DryWetMidi.Tests.csproj --blame --no-build --configuration ReleaseTest --filter "(FullyQualifiedName~Melanchall.DryWetMidi.Tests.Core|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Common|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Composing|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Interaction|FullyQualifiedName~Melanchall.DryWetMidi.Tests.MusicTheory|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Standards|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Tools)" --framework net6.0 --verbosity normal
Build started 7/25/2022 11:29:43 AM.
Test run for /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/bin/ReleaseTest/net6.0/Melanchall.DryWetMidi.Tests.dll (.NETCoreApp,Version=v6.0)
Microsoft (R) Test Execution Command Line Tool Version 17.3.0 (arm64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
The active test run was aborted. Reason: Test host process crashed : Stack overflow.
   at Submission#0+<<Initialize>>d__0.MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon ByRef)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Start[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.__Canon ByRef)
   at Submission#0.<Initialize>()
   at Submission#0.<Factory>(System.Object[])
   at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState+<RunSubmissionsAsync>d__9`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[Microsoft.CodeAnalysis.Scripting.ScriptExecutionState+<RunSubmissionsAsync>d__9`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.CodeAnalysis.Scripting, Version=3.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]](<RunSubmissionsAsync>d__9`1<System.__Canon> ByRef)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Start[[Microsoft.CodeAnalysis.Scripting.ScriptExecutionState+<RunSubmissionsAsync>d__9`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.CodeAnalysis.Scripting, Version=3.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]](<RunSubmissionsAsync>d__9`1<System.__Canon> ByRef)
   at Microsoft.CodeAnalysis.Scripting.ScriptExecutionState.RunSubmissionsAsync[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Collections.Immutable.ImmutableArray`1<System.Func`2<System.Object[],System.Threading.Tasks.Task>>, System.Func`2<System.Object[],System.Threading.Tasks.Task>, System.Runtime.CompilerServices.StrongBox`1<System.Exception>, System.Func`2<System.Exception,Boolean>, System.Threading.CancellationToken)
   at Microsoft.CodeAnalysis.Scripting.Script`1+<RunSubmissionsAsync>d__21[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].MoveNext()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[[Microsoft.CodeAnalysis.Scripting.Script`1+<RunSubmissionsAsync>d__21[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.CodeAnalysis.Scripting, Version=3.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]](<RunSubmissionsAsync>d__21<System.__Canon> ByRef)
   at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].Start[[Microsoft.CodeAnalysis.Scripting.Script`1+<RunSubmissionsAsync>d__21[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], Microsoft.CodeAnalysis.Scripting, Version=3.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]](<RunSubmissionsAsync>d__21<System.__Canon> ByRef)
   at Microsoft.CodeAnalysis.Scripting.Script`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].RunSubmissionsAsync(Microsoft.CodeAnalysis.Scripting.ScriptExecutionState, System.Collections.Immutable.ImmutableArray`1<System.Func`2<System.Object[],System.Threading.Tasks.Task>>, System.Func`2<System.Object[],System.Threading.Tasks.Task>, System.Func`2<System.Exception,Boolean>, System.Threading.CancellationToken)
   at Microsoft.CodeAnalysis.Scripting.Script`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].RunAsync(System.Object, System.Func`2<System.Exception,Boolean>, System.Threading.CancellationToken)
   at Microsoft.CodeAnalysis.Scripting.Script`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].RunAsync(System.Object, System.Threading.CancellationToken)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.RunAsync[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.String, Microsoft.CodeAnalysis.Scripting.ScriptOptions, System.Object, System.Type, System.Threading.CancellationToken)
   at Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.String, Microsoft.CodeAnalysis.Scripting.ScriptOptions, System.Object, System.Type, System.Threading.CancellationToken)
   at Melanchall.DryWetMidi.Tests.Common.TestFilesProvider.GetValidFileReference(System.String, Boolean ByRef)
   at Melanchall.DryWetMidi.Tests.Core.MidiFileTests.CheckValidFilesReadingByReferences()
   at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Span`1<System.Object> ByRef, System.Signature, Boolean, Boolean)
   at System.Reflection.RuntimeMethodInfo.Invoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo)
   at NUnit.Framework.Internal.Reflect.InvokeMethod(System.Reflection.MethodInfo, System.Object, System.Object[])
   at NUnit.Framework.Internal.MethodWrapper.Invoke(System.Object, System.Object[])
   at NUnit.Framework.Internal.Commands.TestMethodCommand.InvokeTestMethod(NUnit.Framework.Internal.TestExecutionContext)
   at NUnit.Framework.Internal.Commands.TestMethodCommand.RunTestMethod(NUnit.Framework.Internal.TestExecutionContext)
   at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(NUnit.Framework.Internal.TestExecutionContext)
   at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand+<>c__DisplayClass1_0.<Execute>b__0()
   at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.RunTestMethodInThreadAbortSafeZone(NUnit.Framework.Internal.TestExecutionContext, System.Action)
   at NUnit.Framework.Internal.Commands.BeforeAndAfterTestCommand.Execute(NUnit.Framework.Internal.TestExecutionContext)
   at NUnit.Framework.Internal.Execution.SimpleWorkItem+<>c__DisplayClass4_0.<PerformWork>b__0()
   at NUnit.Framework.Internal.ContextUtils+<>c__DisplayClass1_0`1[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].<DoIsolated>b__0(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at NUnit.Framework.Internal.ContextUtils.DoIsolated(System.Threading.ContextCallback, System.Object)
   at NUnit.Framework.Internal.ContextUtils.DoIsolated[[System.__Canon, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.Func`1<System.__Canon>)
   at NUnit.Framework.Internal.Execution.SimpleWorkItem.PerformWork()
   at NUnit.Framework.Internal.Execution.WorkItem.RunOnCurrentThread()
   at NUnit.Framework.Internal.Execution.WorkItem.Execute()
   at NUnit.Framework.Internal.Execution.ParallelWorkItemDispatcher.Dispatch(NUnit.Framework.Internal.Execution.WorkItem, NUnit.Framework.Internal.Execution.ParallelExecutionStrategy)
   at NUnit.Framework.Internal.Execution.ParallelWorkItemDispatcher.Dispatch(NUnit.Framework.Internal.Execution.WorkItem)
   at NUnit.Framework.Internal.Execution.CompositeWorkItem.RunChildren()
   at NUnit.Framework.Internal.Execution.CompositeWorkItem.PerformWork()
   at NUnit.Framework.Internal.Execution.WorkItem.RunOnCurrentThread()
   at NUnit.Framework.Internal.Execution.WorkItem.Execute()
   at NUnit.Framework.Internal.Execution.TestWorker.TestWorkerThreadProc()
   at System.Threading.Thread+StartHelper.Callback(System.Object)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback,

The active Test Run was aborted because the host process exited unexpectedly. Please inspect the call stack above, if available, to get more information about where the exception originated from.
The test running when the crash occurred: 
Melanchall.DryWetMidi.Tests.Core.MidiFileTests.CheckValidFilesReadingByReferences

This test may, or may not be the source of the crash.

Attachments:
  /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/TestResults/fa6ea368-4ba8-4e25-83ed-59ce05fdbdd7/Sequence_348532e4fad74c1eb3f2a9cd6efd392c.xml
Passed!  - Failed:     0, Passed:   283, Skipped:     0, Total:   283, Duration: 10 s - /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/bin/ReleaseTest/net6.0/Melanchall.DryWetMidi.Tests.dll (net6.0)
Test Run Aborted with error System.Exception: One or more errors occurred.
 ---> System.Exception: Unable to read beyond the end of the stream.
   at System.IO.BinaryReader.Read7BitEncodedInt()
   at System.IO.BinaryReader.ReadString()
   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.LengthPrefixCommunicationChannel.NotifyDataAvailable()
   at Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.TcpClientExtensions.MessageLoopAsync(TcpClient client, ICommunicationChannel channel, Action`1 errorHandler, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---.

Build FAILED.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:27.26

Sequence_348532e4fad74c1eb3f2a9cd6efd392c.xml.zip

According to the XML it failed on "CheckValidFilesReadingByReferences"

Oh sorry, I gave you command for Windows. Here the correct one:

dotnet test DryWetMidi.Tests/Melanchall.DryWetMidi.Tests.csproj --blame --no-build --configuration ReleaseTest --filter "((FullyQualifiedName~Melanchall.DryWetMidi.Tests.Core|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Common|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Composing|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Interaction|FullyQualifiedName~Melanchall.DryWetMidi.Tests.MusicTheory|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Standards|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Tools)&Name!=CheckValidFilesReadingByReferences)" --framework net6.0 --verbosity normal

Glad it was an easy thing, ha. Looks like core tests run fine:

qadept@QAs-Mac-mini drywetmidi % dotnet test DryWetMidi.Tests/Melanchall.DryWetMidi.Tests.csproj --blame --no-build --configuration ReleaseTest --filter "((FullyQualifiedName~Melanchall.DryWetMidi.Tests.Core|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Common|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Composing|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Interaction|FullyQualifiedName~Melanchall.DryWetMidi.Tests.MusicTheory|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Standards|FullyQualifiedName~Melanchall.DryWetMidi.Tests.Tools)&Name!=CheckValidFilesReadingByReferences)" --framework net6.0 --verbosity normal
Build started 7/25/2022 1:19:58 PM.
Test run for /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/bin/ReleaseTest/net6.0/Melanchall.DryWetMidi.Tests.dll (.NETCoreApp,Version=v6.0)
Microsoft (R) Test Execution Command Line Tool Version 17.3.0 (arm64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:  6317, Skipped:     0, Total:  6317, Duration: 2 m 57 s - /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/bin/ReleaseTest/net6.0/Melanchall.DryWetMidi.Tests.dll (net6.0)

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:03:12.18

OK, the loopback app has this suspicious output when sudo-run (it also returns to terminal after suspending):

qadept@QAs-Mac-mini ~ % sudo ./LoopbackDevice "MIDI A" "MIDI B" "MIDI C" &
[1] 27808
qadept@QAs-Mac-mini ~ % 
[1]  + suspended (tty output)  sudo ./LoopbackDevice "MIDI A" "MIDI B" "MIDI C"
qadept@QAs-Mac-mini ~ % 

When I then proceed to run the Multimedia tests they all fail seemingly from being unable to find the virtual MIDI devices in question:

Error Message:
   System.ArgumentException : There is no output MIDI device 'MIDI A'. (Parameter 'name')

Hmm... What if we remove & at the end of the command?

Without ampersand "command not found":

qadept@QAs-Mac-mini ~ % sudo ./LoopbackDevice "MIDI A" "MIDI B" "MIDI C"  
Password:
sudo: ./LoopbackDevice: command not found
qadept@QAs-Mac-mini ~ % 

With the ampersand it also says that, but looks different (displays a number, maybe process id? and "exit"):

qadept@QAs-Mac-mini ~ % sudo ./LoopbackDevice "MIDI A" "MIDI B" "MIDI C" &
[1] 859
qadept@QAs-Mac-mini ~ % sudo: ./LoopbackDevice: command not found
[1]  + exit 1     sudo ./LoopbackDevice "MIDI A" "MIDI B" "MIDI C"
qadept@QAs-Mac-mini ~ % 

And without "sudo" it states "permission denied", which does suggest it can actually see it:

qadept@QAs-Mac-mini ~ % ./LoopbackDevice "MIDI A" "MIDI B" "MIDI C" & 
[1] 884
zsh: permission denied: ./LoopbackDevice                                        
[1]  + exit 126   ./LoopbackDevice "MIDI A" "MIDI B" "MIDI C"

Is there a particular directory I should run LoopbackDevice from maybe? Tried user folder, downloads, documents.

I double-checked if the Apple Systems >> Security app was blocking anything. Did not state that it was.


The actual error when running the test is:

A total of 1 test files matched the specified pattern.
  Failed CheckEventsReceivingOnConnectedDevices [813 ms]
  Error Message:
   System.ArgumentException : There is no output MIDI device 'MIDI A'. (Parameter 'name')
  Stack Trace:
     at Melanchall.DryWetMidi.Multimedia.OutputDevice.GetByName(String name) in /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi/Multimedia/OutputDevice/OutputDevice.cs:line 448
   at Melanchall.DryWetMidi.Tests.Multimedia.DevicesConnectorTests.CheckEventsReceiving(IReadOnlyList`1 eventsToSend) in /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/Multimedia/DevicesConnector/DevicesConnectorTests.cs:line 147
   at Melanchall.DryWetMidi.Tests.Multimedia.DevicesConnectorTests.CheckEventsReceivingOnConnectedDevices() in /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/Multimedia/DevicesConnector/DevicesConnectorTests.cs:line 32

@ThaddeusChristopher Are you sure you're currently in the folder with LoopbackDevice in Terminal? ./LoopbackDevice means "to run LoopbackDevice placed in the current directory".

So can you please cd to the folder where you've downloaded LoopbackDevice to (or open Terminal from that folder via right-click menu) and try again (I think we don't need ampersand)?

If for some reason you're still seeing the error, I think the best way is to just use Xcode. Source code of the LoopbackDevice is here: LoopbackDevice.c. It's supersimple console program as you can see. So you need to create new console program (I don't remember how the template exactly named in Xcode, maybe something with command line), create new C file and place the code there. Then just run the program from Xcode via the button with triangle. If all is OK, you'll see text output in Xcode stating that virtual ports created. Leave Xcode running the program and now we can try to run multimedia tests.

Thank you for all your efforts to make the library better!

Perhaps that app you compiled on your non-ARM64 processor can't run on this ARM64 processor? Man, Apple really shot themselves in the foot with this new architecture haha.

I feel your pain, but like I said I copied the LoopbackDevice app (extracted from the zip) to various folders and ran it with the results posted above. I can "ls" inside said folders and definitely do see the app present in its extracted form. Further, running without "sudo" denies permission to run it, acknowledging that it is there.

I suspect either (1) it's another ARM64 thing, or (2) Apple is blocking its execution in some way, something they like doing a little too much haha. Or possibly this dev station lacks some tools needed to run it, such as the right shared libraries, etc...

That said, compiling it inside Xcode on this ARM64 processor does appear able to work. We're getting there :-)

So what I was able to do was get it running via Terminal from Xcode's Products >> Build Output folder, like so:

qadept@QAs-Mac-mini Debug % sudo ./midiLoopbackTest1 "MIDI A" "MIDI B" "MIDI C"
Creating client...
Creating port 'MIDI A'...
    creating source...
    creating destination...
OK
Creating port 'MIDI B'...
    creating source...
    creating destination...
OK
Creating port 'MIDI C'...
    creating source...
    creating destination...
OK
Waiting for data...

So... yes, now I can run the Multimedia Tests. Results will be forthcoming...

Results:

Build started 7/26/2022 12:53:51 PM.
Test run for /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/bin/ReleaseTest/net6.0/Melanchall.DryWetMidi.Tests.dll (.NETCoreApp,Version=v6.0)
Microsoft (R) Test Execution Command Line Tool Version 17.3.0 (arm64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
  Skipped CheckInputDevicesEquality_ViaEquals_DifferentDevices_Win [< 1 ms]
  Skipped CheckInputDevicesEquality_ViaEquals_SameDevices_Win [< 1 ms]
  Skipped CheckInputDevicesEquality_ViaOperator_DifferentDevices_Win [< 1 ms]
  Skipped CheckInputDevicesEquality_ViaOperator_SameDevices_Win [< 1 ms]
  Skipped GetInputDeviceProperty_DriverOwner_Win [< 1 ms]
  Skipped GetInputDeviceProperty_DriverVersion_Win [< 1 ms]
  Skipped GetInputDeviceProperty_Manufacturer_Win [< 1 ms]
  Skipped GetInputDeviceProperty_Product_Win [< 1 ms]
  Skipped GetInputDeviceProperty_UniqueId_Win [< 1 ms]
  Skipped GetInputDeviceSupportedProperties_Win [< 1 ms]
  Skipped InputDeviceIsInUse [< 1 ms]
  Skipped CheckOutputDevicesEquality_ViaEquals_DifferentDevices_Win [< 1 ms]
  Skipped CheckOutputDevicesEquality_ViaEquals_SameDevices_Win [< 1 ms]
  Skipped CheckOutputDevicesEquality_ViaOperator_DifferentDevices_Win [< 1 ms]
  Skipped CheckOutputDevicesEquality_ViaOperator_SameDevices_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_Channels_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_DriverOwner_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_DriverVersion_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_Manufacturer_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_NotesNumber_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_Options_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_Product_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_Technology_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_UniqueId_Win [< 1 ms]
  Skipped GetOutputDeviceProperty_VoicesNumber_Win [< 1 ms]
  Skipped GetOutputDeviceSupportedProperties_Win [< 1 ms]
  Skipped OutputDeviceIsInUse [< 1 ms]

Passed!  - Failed:     0, Passed:   516, Skipped:     0, Total:   516, Duration: 21 m 34 s - /Users/qadept/Documents/drywetmidi/drywetmidi/DryWetMidi.Tests/bin/ReleaseTest/net6.0/Melanchall.DryWetMidi.Tests.dll (net6.0)

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:21:36.32

There is a TestResults folder but its output for this run seems to be empty. I'll take it that's a good sign?

I have a proper word for the results – AWESOME! Counts of passed tests are correct:

  • Core: 6317
  • Multimedia: 516

As for LoopbackDevice built by me, I built it using universal format, merging x86_64 and arm64 architectures. But maybe it works fine for dylibs, but need a bit different steps for apps. I've posted the question on Stack Overflow.

Man, Apple really shot themselves in the foot with this new architecture haha.

Absolutely agreed with you :)

Well, I'll build a prerelease NuGet package and let you know.

And many many many thank you, @ThaddeusChristopher. Support of Apple Silicon chips is a critical task in my opinion and I really don't know how it could be solved without you.

Thank you for building such a high quality library in your spare time! And if you ever feel like adding Android/iOS support for modern Net 6 MAUI projects that'd sure be great ;-)

There is a chance that the library will work on iOS due to macOS support since both platforms has similar API. That's just a theory, I didn't test it.

As for Android, it's much more difficult since I even don't know how to setup test environment within my CI process.

But of course I have such plans :)

@ThaddeusChristopher @vorobeyDmitriy @dmitryvorobey The fix is available in a prerelease version of the DryWetMIDI on NuGet – 6.1.2-prerelease3.

@melanchall the nativeless version of this project works on Android and iOS great ;)

🚀 6.1.2 version is released now!

Prerelease NuGet packages will be unlisted soon, so please update the package references to the new version.

Thanks for using the library!