GithubHelp home page GithubHelp logo

melanchall / drywetmidi Goto Github PK

View Code? Open in Web Editor NEW
521.0 24.0 73.0 46.72 MB

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

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

License: MIT License

C# 98.60% PowerShell 0.11% C 1.30%
midi midi-api midi-parser midi-device playback recording

drywetmidi's Introduction

DryWetMIDI Logo

NuGet (full) NuGet (nativeless) Unity asset (full) Unity asset (nativeless)

DryWetMIDI is the .NET library to work with MIDI data and MIDI devices. It allows:

  • Read, write and create Standard MIDI Files (SMF). It is also possible to read RMID files where SMF wrapped to RIFF chunk. You can easily catch specific error when reading or writing MIDI file since all possible errors in a MIDI file are presented as separate exception classes.
  • Send MIDI events to/receive them from MIDI devices, play MIDI data and record it. This APIs support Windows and macOS.
  • Finely adjust process of reading and writing. It allows, for example, to read corrupted files and repair them, or build MIDI file validators.
  • Implement custom meta events and custom chunks that can be written to and read from MIDI files.
  • Manage content of a MIDI file either with low-level objects, like event, or high-level ones, like note (read the High-level data managing section of the library docs).
  • Build musical compositions (see Pattern page of the library docs) and use music theory API (see Music Theory - Overview article).
  • Perform complex tasks like quantizing, notes splitting or converting MIDI file to CSV representation (see Tools page of the library docs).

Please see Getting started section below for quick jump into the library.

Warning
If you want to create an issue or a discussion, read this article first – Support.

Useful Links

Projects using DryWetMIDI

Here the list of noticeable projects that use DryWetMIDI:

  • EMU – Sound to Light Controller
    EMU (DMXIS’s next generation) is a state-of-the-art, intuitive sound-to-light controller designed for professional live musicians and DJs. Easy to use software, EMU allows you to run automated or responsive DMX light shows, leaving you to focus on your show!
  • Musical Bits
    Musical Bits creates software that helps you creating music. Our software uses latest technology, including AI, to model all layers of creativity of a human composer. These layers are implemented as reusable and combinable software components. Musical Bits software is available as co-pilot for producers and composers under the name KLANGMACHT and helps you create, drumsounds, beats, guitars, background choirs, lyrics and more. We even create and distribute full virtual bands, albums and songs. For example, check out the Frostbite Orckings.
  • CoyoteMIDI
    CoyoteMIDI extends the functionality of your MIDI devices to include keyboard and mouse input, including complex key combinations and multi-step macros.
  • Clone Hero
    Free rhythm game, which can be played with any 5 or 6 button guitar controller, game controllers, or just your standard computer keyboard. The game is a clone of Guitar Hero.
  • Electrophonics
    A collection of virtual musical instruments that features real MIDI output.
  • Rustissimo
    Using Rustissimo you can create a concert with your friends and play instruments with synchronization.

If you find that DryWetMIDI has been useful for your project, please put a link to the library in your project's About section or something like that.

Getting Started

Let's see some examples of what you can do with DryWetMIDI. Also you can check out sample applications from CIRCE-EYES (see the profile, VB.NET used).

To read a MIDI file you have to use Read static method of the MidiFile:

var midiFile = MidiFile.Read("Some Great Song.mid");

or, in more advanced form (visit Reading settings page on the library docs to learn more about how to adjust process of reading)

var midiFile = MidiFile.Read(
    "Some Great Song.mid",
    new ReadingSettings
    {
        NoHeaderChunkPolicy = NoHeaderChunkPolicy.Abort,
        CustomChunkTypes = new ChunkTypesCollection
        {
            { typeof(MyCustomChunk), "Cstm" }
        }
    });

To write MIDI data to a file you have to use Write method of the MidiFile:

midiFile.Write("My Great Song.mid");

or, in more advanced form (visit Writing settings page on the library docs to learn more about how to adjust process of writing)

midiFile.Write(
    "My Great Song.mid",
    true,
    MidiFileFormat.SingleTrack,
    new WritingSettings
    {
        UseRunningStatus = true,
        NoteOffAsSilentNoteOn = true
    });

Of course you can create a MIDI file from scratch by creating an instance of the MidiFile and writing it:

var midiFile = new MidiFile(
    new TrackChunk(
        new SetTempoEvent(500000)),
    new TrackChunk(
        new TextEvent("It's just single note track..."),
        new NoteOnEvent((SevenBitNumber)60, (SevenBitNumber)45),
        new NoteOffEvent((SevenBitNumber)60, (SevenBitNumber)0)
        {
            DeltaTime = 400
        }));

midiFile.Write("My Future Great Song.mid");

or

var midiFile = new MidiFile();
TempoMap tempoMap = midiFile.GetTempoMap();

var trackChunk = new TrackChunk();
using (var notesManager = trackChunk.ManageNotes())
{
    NotesCollection notes = notesManager.Objects;
    notes.Add(new Note(
        NoteName.A,
        4,
        LengthConverter.ConvertFrom(
            new MetricTimeSpan(hours: 0, minutes: 0, seconds: 10),
            0,
            tempoMap)));
}

midiFile.Chunks.Add(trackChunk);
midiFile.Write("My Future Great Song.mid");

If you want to speed up playing back a MIDI file by two times you can do it with this code:

foreach (var trackChunk in midiFile.Chunks.OfType<TrackChunk>())
{
    foreach (var setTempoEvent in trackChunk.Events.OfType<SetTempoEvent>())
    {
        setTempoEvent.MicrosecondsPerQuarterNote /= 2;
    }
}

Of course this code is simplified. In practice a MIDI file may not contain SetTempo event which means it has the default one (500,000 microseconds per beat).

Instead of modifying a MIDI file you can use Playback class:

using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
using (var playback = midiFile.GetPlayback(outputDevice))
{
    playback.Speed = 2.0;
    playback.Play();
}

To get duration of a MIDI file as TimeSpan use this code:

TempoMap tempoMap = midiFile.GetTempoMap();
TimeSpan midiFileDuration = midiFile
    .GetTimedEvents()
    .LastOrDefault(e => e.Event is NoteOffEvent)
    ?.TimeAs<MetricTimeSpan>(tempoMap) ?? new MetricTimeSpan();

or simply:

TimeSpan midiFileDuration = midiFile.GetDuration<MetricTimeSpan>();

Suppose you want to remove all C# notes from a MIDI file. It can be done with this code:

foreach (var trackChunk in midiFile.GetTrackChunks())
{
    using (var notesManager = trackChunk.ManageNotes())
    {
        notesManager.Objects.RemoveAll(n => n.NoteName == NoteName.CSharp);
    }
}

or

midiFile.RemoveNotes(n => n.NoteName == NoteName.CSharp);

To get all chords of a MIDI file at 20 seconds from the start of the file write this:

TempoMap tempoMap = midiFile.GetTempoMap();
IEnumerable<Chord> chordsAt20seconds = midiFile
    .GetChords()
    .AtTime(
        new MetricTimeSpan(0, 0, 20),
        tempoMap,
        LengthedObjectPart.Entire);

To create a MIDI file with single note which length will be equal to length of two triplet eighth notes you can use this code:

var midiFile = new MidiFile();
var tempoMap = midiFile.GetTempoMap();

var trackChunk = new TrackChunk();
using (var notesManager = trackChunk.ManageNotes())
{
    var length = LengthConverter.ConvertFrom(
        2 * MusicalTimeSpan.Eighth.Triplet(),
        0,
        tempoMap);
    var note = new Note(NoteName.A, 4, length);
    notesManager.Objects.Add(note);
}

midiFile.Chunks.Add(trackChunk);
midiFile.Write("Single note great song.mid");

You can even build a musical composition:

Pattern pattern = new PatternBuilder()
     
    // Insert a pause of 5 seconds
    .StepForward(new MetricTimeSpan(0, 0, 5))

    // Insert an eighth C# note of the 4th octave
    .Note(Octave.Get(4).CSharp, MusicalTimeSpan.Eighth)

    // Set default note length to triplet eighth and default octave to 5
    .SetNoteLength(MusicalTimeSpan.Eighth.Triplet())
    .SetOctave(Octave.Get(5))

    // Now we can add triplet eighth notes of the 5th octave in a simple way
    .Note(NoteName.A)
    .Note(NoteName.B)
    .Note(NoteName.GSharp)

    // Get pattern
    .Build();

MidiFile midiFile = pattern.ToFile(TempoMap.Default);

DryWetMIDI provides devices API allowing to send MIDI events to and receive them from MIDI devices. Following example shows how to send events to MIDI device and handle them as they are received by the device:

using System;
using Melanchall.DryWetMidi.Multimedia;
using Melanchall.DryWetMidi.Core;

// ...

using (var outputDevice = OutputDevice.GetByName("MIDI Device"))
{
    outputDevice.EventSent += OnEventSent;

    using (var inputDevice = InputDevice.GetByName("MIDI Device"))
    {
        inputDevice.EventReceived += OnEventReceived;
        inputDevice.StartEventsListening();

        outputDevice.SendEvent(new NoteOnEvent());
        outputDevice.SendEvent(new NoteOffEvent());
    }
}

// ...

private void OnEventReceived(object sender, MidiEventReceivedEventArgs e)
{
    var midiDevice = (MidiDevice)sender;
    Console.WriteLine($"Event received from '{midiDevice.Name}' at {DateTime.Now}: {e.Event}");
}

private void OnEventSent(object sender, MidiEventSentEventArgs e)
{
    var midiDevice = (MidiDevice)sender;
    Console.WriteLine($"Event sent to '{midiDevice.Name}' at {DateTime.Now}: {e.Event}");
}

SAST Tools

drywetmidi's People

Contributors

fischertbilligheim avatar laquazi avatar mducharm avatar melanchall avatar muiriswoulfe avatar ronnieoverby avatar somesharp avatar thenathannator avatar troy-f avatar x1nto avatar yiannismaxwell avatar zivillian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

drywetmidi's Issues

Can't install NuGet package through PowerShell

Hi,
I'm trying to install using the NuGet instructions, and Powershell gives this error:
Install-Package : A parameter cannot be found that matches parameter name 'Version'.
At line:1 char:44

  • Try {Install-Package Melanchall.DryWetMidi -Version 4.0.0}Catch {$_ | ...
  •                                        ~~~~~~~~
    
    • CategoryInfo : InvalidArgument: (:) [Install-Package], ParameterBindingException
    • FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.PackageManagement.Cmdlets.InstallPackage

I'm new to .NET, so maybe I'm missing something obvious.

How to use on Unity 2017?

Hi,
Sorry but this is not an issue, but i want to use this library on a Unity project but i dont know how. Do you know if is possible, and how to use?

I found other midi readers but what i want its only a midi file reader, i dont want to play.

Thanks!

Playback produces unexpected results in Unity

I've integrated DWM into my Unity project (2019.1.0f2) for MIDI file playback and parsing. I've called playback.Start() and the MIDI file will not play. I have tried several MIDI files and nothing will play.

// Use the first available OutputDevice (Microsoft GS Wavetable Synth)
var outputDevice = OutputDevice.GetAll().ToArray()[0];
// The MIDI file is copied to this location earlier
var playback = MidiFile.Read(Application.dataPath + @"/Scripts/in.mid").GetPlayback(outputDevice);

Later, in a coroutine (so the MIDI file starts playing at a specific time):

playback.Start();

Playback doesn't want to start, even on the main thread.

Also, playback.Play() works, but it freezes the thread which I don't want, and it also drops a lot of notes, even on really simple MIDI files.

No exceptions are thrown.

What am I missing? And is there some fix to the dropped notes?

Midi routing to multiple devices

Hi Melanchall: is it possible to send received midi events to multiple outputDevices? (Midi Routing) If so, could you provide an example or reference please?.
Thanks.

Incorrect units for MetricTimeSpan

Right, so, still messing around with this library, and I noticed that you are using TotalMicroseconds for MetricTimeSpan, however that is wrong as it is not microseconds, but milliseconds.
Microseconds are 1000 times smaller than milliseconds, heh

React to playback events

So, I was wondering if it's possible to play a MIDI file and not output it to a MIDI device, but instead execute code whenever a MIDI event happens (such as NoteOn).

Events for changing a chunk-event-list

Would it be possible to implement an event-handling for changes in a event-list of a chunk?

I think for using the lists in a datagridview with data-binding this would be necessary.

Regards
Thomas

TimeAs and LengthAs slow

@melanchall I decided to open a separate bug for this since it's not really related to #8. I noticed that with the same test midi file I provided there it takes ~17 sec on my i7 laptop's core to extract each TimedObject's absolute time:
MetricTimeSpan t = event.TimeAs<MetricTimeSpan>(tmap)
and for Notes also:
MetricTimeSpan l = note.LengthAs<MetricTimeSpan>(tmap)

So it's rather slow... a few times slower than my older implementation. Maybe some optimization can be done around recurrent TimeAs / LenghAs calls, some unnecessarily repeated operation can be skipped?

test code:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Melanchall.DryWetMidi.Smf;
using Melanchall.DryWetMidi.Smf.Interaction;

namespace test
{
    class MainClass
    {
        public static void Main()
        {
            Stream stream = new FileStream("/home/dsutyagin/Downloads/test.mid", FileMode.Open, FileAccess.Read);
            MidiFile midiFile = MidiFile.Read(stream);
            Stopwatch stopwatch = Stopwatch.StartNew();
            IEnumerable<ITimedObject> timedObjectsAndNotes = midiFile.GetTimedEvents().ExtractNotes();
            Console.WriteLine(stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();
            TempoMap tmap = midiFile.GetTempoMap();
            Console.WriteLine(stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();
            int i = 0;
            foreach (ITimedObject x in timedObjectsAndNotes)
            {
                // comment me N1 - saves 6 sec
                MetricTimeSpan t = x.TimeAs<MetricTimeSpan>(tmap);

                i++;
                Note note = x as Note;
                if (note != null)
                {
                    i++;

                    //comment me N2 - saves 10 sec
                    MetricTimeSpan l = note.LengthAs<MetricTimeSpan>(tmap);
                }
            }
            Console.WriteLine(stopwatch.ElapsedMilliseconds);
        }
    }
}

sample output:

70
146
16918

Press any key to continue...

Can I use DWM lib in a Windows Form Application?

Sorry to be such a total ignorant time-wasting newbie but how would I use the DryWetMidi package in a Windows Form Application?

I've downloaded the NuGet package and I can see the reference, have added the using Melanchall.DryWetMidi; statements and have added the BuildMoonlightSonata.cs from CodeProject and drywetmidi-examples-createfile.cs from git to my solution but I can't seem to access the methods.

The master build doesn't seem to have any forms (shock! horror!) and I'm all lost without my forms.

Ultimately I'd like to use the MIDI file split functions on a bunch of midi files. So maybe I need to learn WPF?

My apologies for asking such a dumb question.

Thanks

Iterate through noteOn's and get their lengths

Hi @melanchall ! Great lib, trying to use it in my project - I need a visual midi "player" and found that "getTimedEvents" works quite well for this purpose, however one piece of puzzle is missing - I need to know note length for every noteOn - this seems to only be available for generated objects from smf.Interaction, but not from loaded with midifile - is that correct? If so I think we should try to add this functionality. Otherwise please tell me how to do this, maybe it's very easy but I just didn't figure out.

Ability to send raw MIDI messages

Is there any way to send raw MIDI messages using SendEvent() in the form of a byte[] and bypass the object wrappers? Because sometimes the user needs to be able to send a non-predetermined or ambiguous MIDI command, including ones not defined in the standard MIDI spec.

For example the managed-midi library allows you to do this as such:
outputDevice.Send(new byte [] {0x90, 0x40, 0x70}, 0, 3, 0);

I was hoping that drywetmidi has an equivalent function. Thanks!

Volume Support

Does this library support changing volume of notes you want to add to Trackchunk. If it does: How to implement it? If it doesn't: Are there any workarounds?

Thanks in advance

Getting NoteOn time and Length in seconds

Hello,
This is not an issue (sorry), but I am wondering how I can access all NoteOn events, and these attributes:

 Note-on time (seconds),  
 Length of note (seconds),  

I tried the code below, but it's giving incorrect values.

                IEnumerable<Melanchall.DryWetMidi.Smf.Interaction.Note> notes = midi.GetNotes();
                
                var tempoMap = midi.GetTempoMap();
                foreach (var note in notes)
                { 
                    // note's duration (seconds)
                    note.LengthAs<MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000000f

                    // note's start time (seconds)
                    note.TimeAs<MetricTimeSpan>(tempoMap).TotalMicroseconds / 1000000f
                 }

Thanks!

Update wiki to verison 3.0.0

I see that in verison 3.0.0 there's a lot of changes.
Are you going to update Wiki or should I try learn it by myself?

How can I append one midi-file to another?

I would like to append one midi file to another.
I did the following:
I opened the first file (multiple tracks)
I opened the second file (multiple tracks) - and changed the beginning of all tracks to the end of the first midi file.
I added then all tracks of file2 to file1.

That seems not be sufficiant. Do I have to recalculate all delta-times of file2 with the „ticks-per-count-value“ of file1?

Thanks for your support!
Regards
Thomas

Ability to change text encoding post-parse

So right now if you need to try and handle multiple midi files of various text encodings at runtime you are somewhat out of luck and would need to continuously re-parse the whole midi until the correct encoding is found.

Going from this:

public string Text { get; set; }
protected sealed override void ReadContent(MidiReader reader, ReadingSettings settings, int size)
{
    ThrowIfArgument.IsNegative(nameof(size), size,
            "Text event cannot be read since the size is negative number.");

    if (size == 0)
        return;

    textData = reader.ReadBytes(size);
    var encoding = settings.TextEncoding ?? SmfUtilities.DefaultEncoding;
    Text = encoding.GetString(bytes);
}

to something like this (this isnt 100% working code but just something to get an idea of what i'm saying here)

protected byte[] _textData = null;
protected string _textEncoded;

public string Text
{
    get
    {
        if (_textData != null)
        {
            var encoding = settings.TextEncoding ?? SmfUtilities.DefaultEncoding;
            _textEncoded = encoding.GetString(_textData);
            return _textEncoded;

        }
        else
        {
            return _textEncoded;
        }
    }
    set => _textEncoded = value;
}

protected sealed override void ReadContent(MidiReader reader, ReadingSettings settings, int size)
{
    ThrowIfArgument.IsNegative(nameof(size), size, "Text event cannot be read since the size is negative number.");

    if (size == 0)
        return;

    _textData = reader.ReadBytes(size);
}

It would need some other api changes perhaps with how settings and/or encodings are handled.

how to split midi files into windows of x bars

Hey

I am looking through the documentation but I can't seem to find what I need

I want to split a midi file into windows of 8 bars, with a step size of 1 bar

Any suggestions?

Cheers

Issue with EventPlayed EventHandler

Consider the following code snippet:

playManager.EventPlayed += CCEventHandler;

private void CCEventHandler(object sender, MidiEventPlayedEventArgs e)
{
    var ccEvent = e.Event as ControlChangeEvent;
    if (ccEvent != null && ccEvent.ControlNumber == 15 && ccEvent.ControlValue == 0)
    {
        Console.WriteLine("Event triggered");
        playManager.MoveToStart();
    }
}

There is a single CC 15,0 event one bar before the end of my song, but when the playback manager reaches the CC event and jumps in time, it causes the playback manager to go into an infinite loop of playing the previous note (as a continuous, extremely loud tone).

I have tried changing the event to other types including SysEx events with the same result. I can't find any documentation nor sample code on the use of the EventPlayed event handler, so I am stumped for the moment.

Move midi device support into seperate assembly

As mentioned in #28 i think this should be a requirement for #1 due to the current midi device code only supporting windows. (and there being libraries that do support cross platform low level midi device access already such as https://github.com/atsushieno/managed-midi)

The rest of the library already works well cross platform on unity, and at least builds for .net core (haven't done a lot of testing with it on that runtime yet)

TempoEvent Parsing Issue

Hello,

I am using Logic Pro X 10.0.0 to create piano midi files.

Each of these files has a single tempo, and it is found in the last midi event (as a tempo-change event).

I used these midi files in another program, and the playback was fine.
However, Drywetmidi does not seem to correctly parse them and always uses the default (120bpm) tempo.

For a quick fix (untested) I modified the CollectTempoChanges() method in TempoMapManager.cs

 private void CollectTempoChanges()
        {
            int count = 0;
            var lastEvent = _timedEventsManagers.SelectMany(m => m.Events).Last();

            foreach (var tempoEvent in GetTimedEvents(IsTempoEvent))
            {
                count += 1;

                var setTempoEvent = tempoEvent.Event as SetTempoEvent;

                if (setTempoEvent == null)
                    continue;

                // if:
                //   - the last event in the midi file is the tempo event, and
                //   - there is only one tempo event, 
                // then make the time of the tempo event = 0

                // this is so if the last event is the tempo event 
                // we capture that tempo for the entire file 
                // (a fix for Logic Pro X) 

                bool oneTempoChangeAndIsLastEvent = count == 1 && lastEvent == tempoEvent;
                long time = oneTempoChangeAndIsLastEvent ? 0 : tempoEvent.Time;

                TempoMap.Tempo.SetValue(time, new Tempo(setTempoEvent.MicrosecondsPerQuarterNote));
            }
        }

I also attached an example midi file, which was exported from Logic Pro X 10.0.0:
prelude_124bpm.mid.zip

Melanchall.DryWetMidi.dll is built in debug mode

During the Windows App Certification Kit the test failed in the Debug configuration test section

FAILED
Debug configuration

Error Found: The debug configuration test detected the following errors:
    El archivo binario Melanchall.DryWetMidi.dll se crea en modo de depuración.
    which translates...
    The binary file Melanchall.DryWetMidi.dll is built in debug mode
Impact if not fixed: Microsoft Store doesn’t allow a debug version of an app.
How to fix: Please make sure the app isn’t linking to any debug versions of a framework and it is built with release configuration with optimization enabled. If this is a managed app please make sure you have installed the correct version of .NET framework. 

Thanks in advance

Compatibility with Mono

Hi melanchall,
I would like to know if drywetmidi is compatible with Mono. I need to know this because i want to compile my .net application (which uses drywetmidi) with mono in order to run on macos and linux.
Waiting for your kindly response.
Eduardo.

How to send MMC Start event to DAW?

Hi Melanchall,
Is there a way to send an MMC Start event to a DAW?. I want to send the Start (Play) to Cakewalk and Studio One. What event class should i use to do that?
Thanks in advance.

Latency

I am having noticeable latency from when a key is pressed on digital piano and when sound is produced. The latency time is constant (around ~100ms if I had to guess).

Output device is MICROSOFT GS WAVETABLE SYNTH.
The application is running on Unity.

How can I reduce this latency?

  device = Melanchall.DryWetMidi.Devices.InputDevice.GetByName(deviceName);    
  device.EventReceived += OnEventReceived;
  device.StartEventsListening();

 private void OnEventReceived(object sender, MidiEventReceivedEventArgs e)
    {
        if (e.EventType == Melanchall.DryWetMidi.Smf.MidiEventType.NoteOn)
        {
            var note = e as NoteOnEvent;
            Debug.Log(note.Velocity);
            Debug.Log($"{note.GetNoteName()}{note.GetNoteOctave()}");
        }
       
        if (App.Settings.Midi.outputDevice != null)
        {
            App.Settings.Midi.outputDevice.SendEvent(e);
        }
    }
}

thanks!

Bars and Beats gone or renamed?

Originally I was using this code

((NotesCollection)noteMan.Notes).ToList()
   .GroupBy(gb => new {
    Bar = gb.TimeAs<MusicalTimeSpan>(tempoMap).Bars,
    Beat = gb.TimeAs<MusicalTimeSpan>(tempoMap).Beats + 1
})

But now it seems they are red after the update. Is there a way I can get those back?

Problem with "Shift Events"

If a chunk has - as the first entry - not a channel-event (e.g. trackname), then the Playback funktionality does not use the shifted time.

I think not the first entry should be used for the "Shift Event", Maybe the first channel-event should be shifted.

How can I get the "musical" chordname from a chord?

It seems possible to get the "Musical"-notes from a "MusicTheory.Chord" object by using "Parse".
But how can I get the "musical-name" of a "SMF.Interaction.Chord" object?
(Musical Name: e.g: "Cmaj")

Thanks and best regards
Thomas

Creating a midi clock to sync a DryWetMidi app with a DAW?

Hello and thanks for making the library!
I have just started working on creating a midi sequencer using DryWetMidi, and I’m trying to figure out a way to create a midi clock that I can use to synchronize my software with a DAW. What I’m wishing for is something similar to the MidiInternalClock* class in the Sanford Midi library, where some of the features are a way to configure pulses per quarter note, an OnTickEvent (as well as events for Start, Stop and Continue), and a simple way to read and set the current time in ticks. I haven’t really found anything equivalent in DryWetMidi. I’d appreciate any tips or suggestions!

*https://github.com/tebjan/Sanford.Multimedia.Midi/blob/master/Source/Sanford.Multimedia.Midi/Clocks/MidiInternalClock.cs

nuget release

Do you have timeline for the next nuget release? I'm looking for netstandard support.

PatternBuilder SetProgram method not working with ToTrackChunk method

Code to reproduce:

PatternBuilder patternBuilder = new PatternBuilder();
patternBuilder.SetProgram(GeneralMidiProgram.Accordion);
patternBuilder.Note(NoteName.C);
MidiFile midiFile = new MidiFile(new[]
{
    patternBuilder.Build().ToTrackChunk(TempoMap.Default, (FourBitNumber)1)
});

Expected Result: Program index will be changed on channel 1 and note will play on channel 1.
Result: Program index has been changed on channel 0 and note plays on channel 1.

Thank you for any attention brought to this issue. My apologies if this is the result of a misunderstanding of DryWetMIDI on my part.

Non-ASCII encoding is not supported by text-based meta events

From discussion in #1:

@nicowitteman

Hi Max,
I switched to framework, and I think the library is very useful. Now I have this issue:
In a MIDI file bwv227.zip
there is a track named "Ténor". In SequenceTrackNameEvent.Text it is represented as "T?nor". That is a pity, because I want to write the tracks away as files with their trackname. This one fails. Any suggestions?
Nico

@melanchall

Hi Nico,

It is because the Text of any text-based meta event is processed in ASCII encoding. Obviously the é symbol doesn't belong this encoding. I'm planning to add other encodings support later. Maybe as a first iteration I will introduce kind of TextEncoding property in ReadingSettings/WritingSettings so you will be able to specify desired encoding for strings serialization/deserialization. Yes, sounds like a plan.

Thank you for your feedback and for remebering me about non-ASCII encodings in text-based meta events. I'll try to provide a solution in the next release which should be at the beginning of August.

Max

@nicowitteman

Thanks Max, I will await the update.
Nico

suggestion: ProgramChange event in PatternBuilder

I use dryWetMidi to develope a site for building playbacks.
I need every track to be in different instrument, and I build the tracks using PatternBuilder.
If it is possible to add a ProgramChange function to PatternBuilder - I think it will be usefull not just for me...

Events-Collection - RemoveAt

Should the „removeAt“ or „remove“ of the EventsCollection not update the delta-time of the following event?

Regards
Thomas

Convert Format-0 to Format-1

How can I convert a Format-0 Chunk to several Chunks - splitted by the midi-channel?

I created this Code, but I assume this can be realized in a much bettre way:

       // Create an array with 16 entries
        var timedEvents = FourBitNumber.Values.Select(channel => new List<ChannelEvent>()).ToArray();
            
            foreach (TrackChunk track in gMidiFile.GetTrackChunks())
            {
            long lTime = 0;

                foreach (MidiEvent ev in track.Events)
                {
                lTime += ev.DeltaTime;

                    if (ev is ChannelEvent)
                    {
                        ev.DeltaTime = lTime;    // Use absolute times to split
                        timedEvents[((ChannelEvent)ev).Channel].Add((ChannelEvent)ev);
                    }
                }

            track.Events.RemoveAll(ev => ev is ChannelEvent);
        }

        TrackChunk[] tcarray = new TrackChunk[16];

        int index = 0;

        for (int i = 0; i < 16; i++)
        {
            //---------------------------------------------
            //Change the absolute times back to delta-time
            //---------------------------------------------
            ChannelEvent[] cev = (ChannelEvent[]) timedEvents[i].ToArray();

            if (cev.Length == 0)
                continue;
            //------------------------------------------
            // calculate deltas
            //------------------------------------------
            long[] deltas = new long[cev.Length];
            deltas[0] = cev[0].DeltaTime;

            for (int j = 1; j < cev.Length; j++)
                deltas[j] = cev[j].DeltaTime - cev[j - 1].DeltaTime;

            //------------------------------------------
            // copy deltas to events
            //------------------------------------------
            for (int k = 0; k < cev.Length; k++)
                  cev[k].DeltaTime = deltas[k];

            // Create a new TrackChunk with the events
            tcarray[index] = new TrackChunk(timedEvents[i]);
            index++;
        }

        gMidiFile.Chunks.AddRange(tcarray);

Automatic chords - is it possible?

Thanks for this amazing library!
Did you write a code to create automatic chords?
I mean, If I want to use your library to make a playback. Do I have to enter code for all the sequences (drums, piano, guitar etc.) Or you have a better way to suggest me?
Thanks again!

Fractional Beats?

First… amazing library! Thank you, very much.

I want to use drywetmidi to playback midi events. It seems that my source event times and lengths are not quite compatible with yours.

For example, my program is generating midi events with the following structure:

struct Event<T>
{
	public int RelativeBar { get; set; }
	public double Position { get; set; }
	public double Length { get; set; }
	public T Data { get; set; }
}

RelativeBar: Simply the bar that an event starts.
Position: The beat that an event starts. 1 would mean first beat. 2.5 would mean halfway before between 2nd and 3rd beat.
Length: The duration of the event in beats. '1.33' would mean roughly 1 + 1/3 beats.

You can see that I'm representing the number of beats as a double where the value represents {Beats}.{Cents}. I borrowed this system directly from my favorite DAW, Reaper:

image

It doesn't seem that BarBeatTimeSpan offers parity with this representation. It has Bars/Beats/Ticks. Beats is an whole number and Ticks seems to store an additional MidiTimeSpan value that doesn't really have anything to do with bars/beats.

I'm sure that I can do the math on my own to convert my fractional beats to your ITimeSpan but I wanted to go ahead and ask you if I was missing something in the library that handles this for me.

Thanks for your help!

Incoming events - how to get sustain pedal data?

I am listening to the InputDevice and want to get the noteName and velocity from NoteOn, NoteOff events, and also the sustain pedal event (on piano).

I know the EventType, but don't know what to do next:

       if (e.EventType == DryWetMidi.Smf.MidiEventType.NoteOn)
       {
           // -> NoteName ???
           // -> velocity ???
       }

thank you

Total length of MIDI file

Hey @melanchall,

Thanks for providing this library!

How would you go about getting the total length of a MIDI file?
I've looked at the example you've provided in the README, but this gives you the timing of the last note-off event:

TempoMap tempoMap = midiFile.GetTempoMap();

TimeSpan midiFileDuration = midiFile.GetTimedEvents().LastOrDefault(e => e.Event is NoteOffEvent)?.TimeAs<MetricTimeSpan>(tempoMap) ?? new MetricTimeSpan();

I need the length of the file including any "unused space".

2bars

In the scenario above, what I'm looking for is the value 768 ticks (96 ticks * 8 beats), but if I use the provided code, I get 696 (96 ticks * 7,25 beats (the position of the cursor)).

I've tried to look through the wiki, but I can't seem to figure it out.

Not Exact an issue- Get note time near to song time

Hi,

Me again and now with my game come to be ready soon using your source <3
Take a look: https://www.youtube.com/watch?v=fqLiBD0p2cI

But i am not here only to show you the game, but to ask about notes.

As you can see on some notes in this video, some notes next to other one have different distances, where on midi the distance is equals.

I think the way i using to get notes is not the best, but the song time is not the exat time of note, then, i cant get the note form midi only quering the song time, i need to get next nearest note based on last note time and new time in a interval.

Is there a best way to get the exat note using the song time?

I have created a Dictionary to cache the notes for difficulties based on note time. The way i get the note is:

tempNotes.Where(pairs => pairs.Key == actualTime || pairs.Key > lastTime && pairs.Key < position).ToList().ForEach(pairs =>
{
  ...
  lastTime = actualTime;
}

Where the key is the note time.
And i run this loop on every frame.

If you know a better way to get and put the note time on my Disctionay or a better way to get the next note, ill glad if you can help.

Thank you!

Edit: This is only with too near notes, with other notes theres no problems.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.