diff --git a/.azure/workflows/azure-ci-development.yml b/.azure/workflows/azure-ci-development.yml index f5ae940..4a7d210 100644 --- a/.azure/workflows/azure-ci-development.yml +++ b/.azure/workflows/azure-ci-development.yml @@ -20,9 +20,9 @@ steps: inputs: version: '3.1.x' - task: UseDotNet@2 - displayName: 'Install Net 6' + displayName: 'Install Net 7' inputs: - version: '6.0.x' + version: '7.0.x' # SonarCloud Prepare Analysis - task: SonarCloudPrepare@1 @@ -31,8 +31,8 @@ steps: SonarCloud: 'SonarCloud Daniel127' organization: 'daniel127' scannerMode: 'MSBuild' - projectKey: 'rtmidi-dotnet' - projectName: 'rtmidi-dotnet' + projectKey: 'Daniel127_RtMidi.Net' + projectName: 'RtMidi.Net' extraProperties: | sonar.exclusions=**/obj/**,**/*.dll sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/coverage/coverage.opencover.xml diff --git a/.azure/workflows/azure-ci-release.yml b/.azure/workflows/azure-ci-release.yml index 3b6c3fd..7fa96e9 100644 --- a/.azure/workflows/azure-ci-release.yml +++ b/.azure/workflows/azure-ci-release.yml @@ -24,9 +24,9 @@ steps: inputs: version: '3.1.x' - task: UseDotNet@2 - displayName: 'Install Net 6' + displayName: 'Install Net 7' inputs: - version: '6.0.x' + version: '7.0.x' # SonarCloud Prepare Analysis - task: SonarCloudPrepare@1 @@ -35,8 +35,8 @@ steps: SonarCloud: 'SonarCloud Daniel127' organization: 'daniel127' scannerMode: 'MSBuild' - projectKey: 'rtmidi-dotnet' - projectName: 'rtmidi-dotnet' + projectKey: 'Daniel127_RtMidi.Net' + projectName: 'RtMidi.Net' extraProperties: | sonar.exclusions=**/obj/**,**/*.dll sonar.cs.opencover.reportsPaths=$(Agent.TempDirectory)/coverage/coverage.opencover.xml diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index 73d2403..1593739 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -10,6 +10,6 @@ jobs: - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 6.0.x + dotnet-version: 7.0.x - name: Build with dotnet - run: dotnet build --configuration Debug + run: dotnet build --configuration Release diff --git a/RtMidi.Net.sln b/RtMidi.Net.sln index f4b9f45..7a676ee 100644 --- a/RtMidi.Net.sln +++ b/RtMidi.Net.sln @@ -3,9 +3,15 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RtMidi.Net", "RtMidi.Net\RtMidi.Net.csproj", "{4A4EAB7A-9B1A-4030-B94B-4A5EC19DECBE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RtMidi.Net", "RtMidi.Net\RtMidi.Net.csproj", "{4A4EAB7A-9B1A-4030-B94B-4A5EC19DECBE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkerTest", "WorkerTest\WorkerTest.csproj", "{54B7CA1F-3964-492C-8251-44A5A244D7C8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkerTest", "WorkerTest\WorkerTest.csproj", "{54B7CA1F-3964-492C-8251-44A5A244D7C8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{19D1F538-EE06-41DD-ABF5-6488D3903B51}" + ProjectSection(SolutionItems) = preProject + LICENSE.md = LICENSE.md + README.md = README.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/RtMidi.Net/Clients/MidiClient.cs b/RtMidi.Net/Clients/MidiClient.cs index c99b053..1136f20 100644 --- a/RtMidi.Net/Clients/MidiClient.cs +++ b/RtMidi.Net/Clients/MidiClient.cs @@ -1,13 +1,13 @@ using RtMidi.Net.Enums; using RtMidi.Net.InteropServices; -namespace RtMidi.Net; +namespace RtMidi.Net.Clients; public abstract class MidiClient : IDisposable { private readonly MidiDeviceInfo _deviceInfo; internal readonly RtMidiBase RtMidiClient; - private bool disposedValue; + private bool _disposedValue; internal MidiClient(uint deviceId, RtMidiBase rtMidiClient) { @@ -43,13 +43,13 @@ public void Close() protected virtual void Dispose(bool disposing) { - if (!disposedValue) + if (!_disposedValue) { if (disposing) { RtMidiClient.Dispose(); } - disposedValue = true; + _disposedValue = true; } } diff --git a/RtMidi.Net/Clients/MidiInputClient.cs b/RtMidi.Net/Clients/MidiInputClient.cs index 7d641b5..f3f41b2 100644 --- a/RtMidi.Net/Clients/MidiInputClient.cs +++ b/RtMidi.Net/Clients/MidiInputClient.cs @@ -1,8 +1,9 @@ +using System.Diagnostics; using RtMidi.Net.Enums; using RtMidi.Net.Events; using RtMidi.Net.InteropServices; -namespace RtMidi.Net; +namespace RtMidi.Net.Clients; public class MidiInputClient : MidiClient { @@ -74,10 +75,40 @@ private void InternalMessageReceived(double timestamp, IntPtr messagePtr, UIntPt public (MidiMessage, TimeSpan) GetMessage() { - var (messageData, timestamp) = _rtMidiInClient.GetMessage(); - return (ConvertMessage(messageData), TimeSpan.FromSeconds(timestamp)); + Stopwatch stopwatch = new(); + byte[] messageData; + stopwatch.Start(); + do + { + (messageData, _) = _rtMidiInClient.GetMessage(); + } while (messageData.Length == 0); + stopwatch.Stop(); + var timestamp = TimeSpan.FromSeconds(stopwatch.Elapsed.TotalSeconds); + return (ConvertMessage(messageData), timestamp); } + public async Task<(MidiMessage, TimeSpan)> GetMessageAsync(CancellationToken cancellationToken) + { + return await Task.Run(() => + { + Stopwatch stopwatch = new(); + byte[] messageData; + + stopwatch.Start(); + do + { + (messageData, _) = _rtMidiInClient.GetMessage(); + } while (!cancellationToken.IsCancellationRequested && messageData.Length == 0); + stopwatch.Stop(); + + cancellationToken.ThrowIfCancellationRequested(); + + var timestamp = TimeSpan.FromSeconds(stopwatch.Elapsed.TotalSeconds); + return (ConvertMessage(messageData), timestamp); + }, cancellationToken); + } + + private static MidiMessage ConvertMessage(IReadOnlyList message) { MidiMessageType type; diff --git a/RtMidi.Net/Clients/MidiOutputClient.cs b/RtMidi.Net/Clients/MidiOutputClient.cs index 284a1e0..b231b9f 100644 --- a/RtMidi.Net/Clients/MidiOutputClient.cs +++ b/RtMidi.Net/Clients/MidiOutputClient.cs @@ -1,7 +1,7 @@ using RtMidi.Net.Enums; using RtMidi.Net.InteropServices; -namespace RtMidi.Net; +namespace RtMidi.Net.Clients; public class MidiOutputClient : MidiClient { diff --git a/RtMidi.Net/RtMidi.Net.csproj b/RtMidi.Net/RtMidi.Net.csproj index 8596925..8a94757 100644 --- a/RtMidi.Net/RtMidi.Net.csproj +++ b/RtMidi.Net/RtMidi.Net.csproj @@ -1,6 +1,6 @@ - net6.0 + net6.0;net7.0 enable enable diff --git a/WorkerTest/Worker.cs b/WorkerTest/Worker.cs index 0e89d0c..cc9da5c 100644 --- a/WorkerTest/Worker.cs +++ b/WorkerTest/Worker.cs @@ -1,4 +1,7 @@ using RtMidi.Net; +using RtMidi.Net.Clients; +using RtMidi.Net.Enums; +using RtMidi.Net.Events; namespace WorkerTest { @@ -16,9 +19,9 @@ public Worker(ILogger logger) _logger = logger; } - - public override Task StartAsync(CancellationToken cancellationToken) + public override async Task StartAsync(CancellationToken cancellationToken) { + _logger.LogInformation("Starting"); var devices = MidiManager.GetAvailableDevices(); foreach (var d in devices) { @@ -26,39 +29,58 @@ public override Task StartAsync(CancellationToken cancellationToken) } if (devices.Any()) { - //TODO Change device to test - var devicePort = 0u; - var device = MidiManager.GetDeviceInfo(devicePort, RtMidi.Net.Enums.MidiDeviceType.Input); + var devicePort = 1u; //TODO Change device to test + var device = MidiManager.GetDeviceInfo(devicePort, MidiDeviceType.Input); _midiInputClient = new MidiInputClient(device); - _midiInputClient.OnMessageReceived += MidiClient_OnMessageReceived; - _midiInputClient.ActivateMessageReceivedEvent(); + //TODO The event throws an exception after a while, i'm not sure why + //_midiInputClient.OnMessageReceived += MidiClient_OnMessageReceived; + //_midiInputClient.ActivateMessageReceivedEvent(); _midiInputClient.Open(); } - return base.StartAsync(cancellationToken); + else + { + var exception = new Exception("No Midi devices found"); + _logger.LogError(exception, "No Midi devices found"); + throw exception; + } + + await base.StartAsync(cancellationToken); } - protected override Task ExecuteAsync(CancellationToken stoppingToken) + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - return Task.CompletedTask; + _logger.LogInformation("Executing"); + while (!stoppingToken.IsCancellationRequested) + { + var (message, _) = await _midiInputClient!.GetMessageAsync(stoppingToken); + OnMessageReceived(message); + } } - public override Task StopAsync(CancellationToken cancellationToken) + public override async Task StopAsync(CancellationToken cancellationToken) { + _logger.LogInformation("Stopping"); _midiInputClient?.Close(); _midiInputClient?.Dispose(); - return base.StopAsync(cancellationToken); + await base.StopAsync(cancellationToken); } + private void MidiClient_OnMessageReceived(object? sender, MidiMessageReceivedEventArgs args) + { + OnMessageReceived(args.Message); + } - private void MidiClient_OnMessageReceived(object? sender, RtMidi.Net.Events.MidiMessageReceivedEventArgs args) + private void OnMessageReceived(MidiMessage midiMessage) { - if(args.Message is MidiMessageNote message) + if (midiMessage is MidiMessageNote { Type: MidiMessageType.NoteOn or MidiMessageType.NoteOff } message) { - _logger.LogInformation($"{message.Note.GetName()} - {message.Type}"); + const int offset = 21; + var note = message.Note.GetByteRepresentation() - offset; + _logger.LogInformation("{noteName} - {messageType}, Note number: {noteNumber}", message.Note.GetName(), message.Type, note); } else { - _logger.LogInformation($"{args.Message.Type}"); + _logger.LogInformation("{messageType}", midiMessage.Type); } } } diff --git a/WorkerTest/WorkerTest.csproj b/WorkerTest/WorkerTest.csproj index e5f1ef8..f8c5662 100644 --- a/WorkerTest/WorkerTest.csproj +++ b/WorkerTest/WorkerTest.csproj @@ -1,14 +1,14 @@ - + - net6.0 + net7.0 enable enable dotnet-WorkerTest-B41C8CE5-6C97-4BF3-90FB-7FB7452FA332 - + @@ -16,6 +16,9 @@ + + Always + Always diff --git a/WorkerTest/librtmidi.so b/WorkerTest/librtmidi.so new file mode 100644 index 0000000..72d0ab1 Binary files /dev/null and b/WorkerTest/librtmidi.so differ